class FirstClass: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
SecondClass()
}
}
class TableView: FirstClass {
var bodyTableView1: UITableView!
override init() {
super.init(nibName: nil, bundle: nil)
bodyTableView1 = UITableView(frame: CGRectMake(0, 0, 250, 250 ))
bodyTableView1.backgroundColor = UIColor.whiteColor()
self.view.addSubview(bodyTableView1)
}
}
I've tried many ways but managed not to add anything from the second class.
If you could give me an example of the most basic is the appreciate.
Thank you!
Pls Modify Your TableView Like Below:
class TableView: UIView,UITableViewDataSource,UITableViewDelegate
{
var vc:UIViewController!
var bodyTableView1: UITableView!
func addActionBar(vc:UIViewController)
{
self.vc=vc;
bodyTableView1 = UITableView(frame: CGRectMake(0, 0, 250, 250 ))
bodyTableView1.backgroundColor = UIColor.whiteColor()
bodyTableView1.dataSource = self
bodyTableView1.delegate = self
vc.view.addSubview(bodyTableView1)
}
//Pls add tableview delegate methods here
}
And also add below code in FirstClass:
class FirstClass: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var ab : TableView = TableView(frame: CGRectMake(0, 0, 250, 250))
self.view.addSubview(ab)
ab.addActionBar(self)
}
}
Related
Below is my code in view controller
import Cocoa
class ViewController : NSViewController, NSTableViewDelegate, NSTableViewDataSource {
let stepperBtn: NSSegmentedControl = {
let stepperBtn = NSSegmentedControl()
stepperBtn.segmentCount = 2
stepperBtn.setImage(NSImage(systemSymbolName: "plus.circle", accessibilityDescription: "plus"), forSegment: 0)
stepperBtn.setImage(NSImage(systemSymbolName: "minus.circle", accessibilityDescription: "minus"), forSegment: 1)
stepperBtn.translatesAutoresizingMaskIntoConstraints = false
return stepperBtn
}()
var tableView: NSTableView {
let tablview = NSTableView()
tablview.translatesAutoresizingMaskIntoConstraints = false
return tablview
}
override func loadView() {
self.view = NSView(frame: NSMakeRect(0.0, 0.0, 550.0, 300.0))
tableView.delegate = self
tableView.dataSource = self
self.view.addSubview(tableView)
self.view.addSubview(stepperBtn)
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
override func viewDidLayout() {
}
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfRows(in tableView: NSTableView) -> Int {
return 5
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I get below error : "NSTableView:0x7f9beb851600.top"> and <NSLayoutYAxisAnchor:0x600000bcc400 "NSView:0x7f9beaa184e0.top"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.
How to add constraints programatically to NSTableView
I want to implement a popup notification into my app when data was being updated successfully or not. To do that I:
created a .xib file: Screenshot
created a class where I load that NIB:
import UIKit
class PopupView: UIView {
static let instance = PopupView()
#IBOutlet weak var backgroundView: UIView!
#IBOutlet weak var popupView: UIVisualEffectView!
#IBOutlet weak var symbol: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("PopupView", owner: self)
popupView.layer.cornerRadius = 20
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showPopup(title: String, message: String, symbol: UIImage, on viewController: UIViewController) {
self.titleLabel.text = title
self.descriptionLabel.text = message
self.symbol.image = symbol
guard let targetView = viewController.view else { return }
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
}
In the above class I created a showPopup method where defined a backgroundView frame to be equal to ViewController bounds.
When I call that method in desired ViewController I receive the behaviour where my popupView shows itself and then went off the screen straight away (black area in the GIF): GIF
Would you be so kind to help me understand and fix the reason why the popupView went off the screen and not just equal to a ViewController bounds.
The Code after Shawn Frank's answer
PopupView class:
import UIKit
class PopupView: UIView {
#IBOutlet weak var popupView: UIVisualEffectView!
#IBOutlet weak var symbol: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
if let views = Bundle.main.loadNibNamed("PopupView", owner: self) {
guard let view = views.first as? UIView else { return }
view.frame = bounds
addSubview(view)
}
}
func showPopup(title: String, message: String, symbol: UIImage, on viewController: UIViewController) {
titleLabel.text = title
descriptionLabel.text = message
self.symbol.image = symbol
popupView.layer.cornerRadius = 20
popupView.clipsToBounds = true
viewController.view.addSubview(self)
UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveLinear) {
self.popupView.center.y += 40
} completion: { _ in
UIView.animate(withDuration: 0.3, delay: 3.0, options: .curveLinear) {
self.popupView.center.y -= 80
} completion: { _ in
self.popupView.removeFromSuperview()
}
}
}
}
Call in desired ViewController:
let width = CGFloat(10)
let midX = (self.view.frame.width - width) / 2.0
let frame = CGRect(x: midX, y: 0, width: width, height: 135)
let popup = PopupView(frame: frame)
popup.showPopup(title: "Test", message: "Tested super test", symbol: UIImage(named: "checkmark.circle.fill")!, on: self)
Constraints in xib: Screenshot
Current result: GIF
I do not use XIB and storyboard too much these days so I also had to refresh my memory and I used this tutorial
This is the custom PopupView class, I prefer not to use singleton for this but it is my personal preference
class PopupView: UIView {
private let xibName = "PopupView"
#IBOutlet weak var visualEffectBackground: UIVisualEffectView!
#IBOutlet weak var titleLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
if let views = Bundle.main.loadNibNamed(xibName, owner: self),
let popupView = views.first as? UIView {
popupView.frame = bounds
addSubview(popupView)
}
}
func showPopup(title: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else { return }
titleLabel.text = title
layer.cornerRadius = 20
clipsToBounds = true
targetView.addSubview(self)
}
}
XIB is set up like this:
Then in the view controller:
#IBAction func didTapPopUp(_ sender: Any) {
// Give your own width, height etc
let width = CGFloat(180)
let midX = (view.frame.width - width) / 2.0
let frame = CGRect(x: midX, y: 100, width: width, height: 80)
let popup = PopupView(frame: frame)
popup.showPopup(title: "Hello", on: self)
}
Gives me this result:
Update based on artexhibit (OPs) comments
The custom view can get its frames in 3 ways that I can think of:
Directly from XIB
By providing frame during programmatic initialization
From the frame set when creating the view in storyboard
To make the view work for all these scenarios, we should not do any frame adjustment inside the custom view and leave it to the parent / container / superview
So I made the following changes to work for all scenarios:
class PopupView: UIView {
private static let xibName = "PopupView"
#IBOutlet weak var visualEffectBackground: UIVisualEffectView!
#IBOutlet weak var titleLabel: UILabel!
init() {
super.init(frame: .zero)
configure()
}
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func initializeWithNib() {
var popupView = UIView()
if let views = Bundle.main.loadNibNamed(PopupView.xibName,
owner: self),
let view = views.first as? UIView {
popupView = view
}
frame = popupView.bounds
addSubview(popupView)
}
private func initializeWithFrame() {
if let views = Bundle.main.loadNibNamed(PopupView.xibName,
owner: self),
let popupView = views.first as? UIView {
popupView.frame = bounds
addSubview(popupView)
}
}
private func configure() {
if frame == .zero {
initializeWithNib()
}
else {
initializeWithFrame()
}
layer.cornerRadius = 20
clipsToBounds = true
}
func showPopup(title: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else { return }
print(frame)
titleLabel.text = title
targetView.addSubview(self)
}
}
Now this has the flexibility to work with all 3 scenarios:
// In code
#IBAction func didTapPopUp(_ sender: Any) {
// Default initializer
let popup = PopupView()
var originX = (view.frame.width - popup.frame.width) / 2.0
popup.frame.origin = CGPoint(x: originX, y: 100)
popup.showPopup(title: "Hello", on: self)
// Frame initializer
let popupWidth: CGFloat = 200
let popupHeight: CGFloat = 100
originX = (view.frame.width - popupWidth) / 2.0
let originY = (view.frame.height - popupHeight) / 2.0
let popupFrame = CGRect(x: originX,
y: originY,
width: popupWidth,
height: popupHeight)
let popupTwo = PopupView(frame: popupFrame)
popupTwo.showPopup(title: "Frames", on: self)
}
From storyboard
This gives the following results
My test app have 4 ScrollView.
UIScrollView which can scroll up/down.
UIPageViewController inside 1, which can scroll left/right.
UITableView inside one of viewController 2, can scroll up/down.
UIScrollView inside UITableViewCell on tableView 3, can scroll left/right.
https://i.stack.imgur.com/BexX3.jpg
First swipe gesture by UIScrollView 4 causes PageViewController 2 scroll instead. Second and more swipes work correct.
PanGestureRecognizer of UIScrollView 4 has a state "failed" on the first swipe.
<UIScrollViewPanGestureRecognizer: 0x7f826881fd50; state = Failed; delaysTouchesEnded = NO; view = <Tests.MyCellScroll 0x7f826802f600>; target= <(action=handlePan:, target=<Tests.MyCellScroll 0x7f826802f600>)>>
Any ideas what’s the problem or how could it be debugged?
Example
class ViewController: UIViewController {
private let vcs: [UIViewController] = [
MyVC(),
UIViewController()
]
private lazy var scroll: UIScrollView = {
let scroll = UIScrollView()
scroll.backgroundColor = .systemPink
return scroll
}()
private lazy var pageVC: UIPageViewController = {
let page = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
page.view.backgroundColor = .white
page.delegate = self
page.dataSource = self
return page
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.scroll)
self.addChild(self.pageVC)
self.scroll.addSubview(self.pageVC.view)
self.pageVC.didMove(toParent: self)
self.pageVC.setViewControllers([self.vcs[0]], direction: .forward, animated: true, completion: nil)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.scroll.frame = self.view.frame
self.scroll.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 50)
self.pageVC.view.frame = CGRect(x: self.scroll.bounds.minX, y: self.scroll.bounds.minY + 250, width: self.scroll.bounds.width, height: self.scroll.contentSize.height - 250)
}
}
extension ViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
self.vcs.firstIndex(of: viewController) == 1 ? self.vcs[0] : nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
self.vcs.firstIndex(of: viewController) == 0 ? self.vcs[1] : nil
}
}
final class MyVC: UIViewController {
private lazy var table: UITableView = {
let table = UITableView()
table.register(MyCell.self, forCellReuseIdentifier: String(describing: MyCell.self))
table.delegate = self
table.dataSource = self
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.table)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.table.frame = self.view.frame
}
}
extension MyVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 5 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { MyCell() }
}
final class MyCell: UITableViewCell {
private lazy var subview: UIView = {
let view = UIView()
view.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.7)
return view
}()
private lazy var scrollView: MyCellScroll = {
let view = MyCellScroll()
return view
}()
init(){
super.init(style: .default, reuseIdentifier: nil)
self.contentView.backgroundColor = UIColor.systemOrange.withAlphaComponent(0.7)
self.contentView.addSubview(self.scrollView)
self.scrollView.addSubview(self.subview)
}
override func layoutSubviews() {
super.layoutSubviews()
let frame = self.contentView.frame
self.scrollView.frame = frame
self.scrollView.contentSize = CGSize(width: 500, height: frame.height)
self.subview.frame = CGRect(x: 16, y: 8, width: 500 - 32, height: frame.height - 16)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
final class MyCellScroll: UIScrollView, UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
print(gestureRecognizer)
return false // 5
}
}
Although I could create a func setImages () in my CustomView class and call this after initializing myCustomView, I would like to understand if there's a cleaner way to set a view's delegate so that it is accessible when being initialized.
My main ViewController contains
class Main: UIViewController, CustomViewDelegate {
var imagesArray:[UIImage] = [Image1,Image2,Image3,Image4,Image5]
var myCustomView = CustomView()
override func viewDidLoad() {
super.viewDidLoad()
myCustomView.delegate = self
myCustomView = CustomView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
//this causes init of CustomView, but delegate is now nil and button images don't load
}
}
My CustomView file contains
var buttonsArray = [Button1,Button2,Button3,Button4,Button5]
override init(frame: CGRect) {
super.init(frame : frame)
for n in 0..< buttonsArray.count {
buttonsArray[n].setImage(delegate?.imagesArray[n], for: .normal)
}
}
You can create a new initializer which takes a frame and a delegate type and set the delegate before you set images to buttons
init(frame: CGRect,sender: CustomViewDelegate) {
super.init(frame : frame)
self.delegate = sender
for n in 0..< buttonsArray.count {
buttonsArray[n].setImage(delegate?.imagesArray[n], for: .normal)
}
}
For that, you have to confirm your viewController for the delegate(obviously).Call your customView like this in viewController:
class ViewController: UIViewController, CustomViewDelegate {
var myCustomView: CustomView!
override func viewDidLoad() {
myCustomView = CustomView(frame: self.view.bounds, sender: self)
}
}
hope it helps!
I want to modify an object of child UIViewController but it is not working. Can anyone solve my problem? thanks a lot ~~
below is my code
My ViewController class:
import UIKit
class ViewController: UIViewController {
var x:UIView?
override func viewDidLoad() {
super.viewDidLoad()
var sec = second()
sec.x?.backgroundColor = UIColor.redColor()
view.addSubview(sec.view)
self.addChildViewController(sec)
}
}
My second class:
class second:UIViewController{
var x:UIView!
override func viewDidLoad() {
super.viewDidLoad()
x = UIView(frame: CGRectMake(55, 55, 100, 100))
x!.backgroundColor = UIColor.greenColor()
self.view.addSubview(x!)
}
}
This is happening because the view is probably nil when you are attempting to set its background color. Just add a background color property inside the second view controller and set that. Then inside viewDidLoad set the second view controller's background color from there.
Something like this
class ViewController: UIViewController {
var x:UIView?
override func viewDidLoad() {
super.viewDidLoad()
var sec = second()
sec.backColor = UIColor.redColor()
view.addSubview(sec.view)
self.addChildViewController(sec)
}
}
And then:
class second:UIViewController {
var backColor = UIColor.greenColor()
var x:UIView!
override func viewDidLoad() {
super.viewDidLoad()
x = UIView(frame: CGRectMake(55, 55, 100, 100))
x.backgroundColor = backColor
self.view.addSubview(x)
}
}
After initializing second view controller, call addChildViewController(instanceOfSecondVC)
And after adding it as subview, call sec.didMoveToParentViewController(self).
So your code will be like:
import UIKit
class ViewController: UIViewController {
var x:UIView?
override func viewDidLoad() {
super.viewDidLoad()
var sec = second()
addChildViewController(sec)
sec.x?.backgroundColor = UIColor.redColor()
view.addSubview(sec.view)
self.addChildViewController(sec)
sec.didMoveToParentViewController(self)
}
}