How to pass UIViewController to another class in Swift?
I have SomeViewController class:
class RidingViewController: UIViewController {
fileprivate let header = RidingViewHeader(controller: self)
override func viewDidLoad() {
super.viewDidLoad()
header.setNavigationBar()
}
...
}
And I want to separate some code and to set up its header in the another class. I've created this class
class RidingViewHeader {
var controller: UIViewController
let navigationBar= UINavigationBar()
let navigationItem = UINavigationItem()
init(controller: UIViewController) {
self.controller = controller
}
...
}
In this case I get an error:
Cannot convert value of type '(NSObject) -> () -> UIViewController' to expected argument type 'UIViewController'
What is the better way of doing it?
You can't access self until the view controller has been initialized. You could make it a lazy variable:
fileprivate lazy var header: RidingViewHeader = {
return RidingViewHeader(controller: self)
}()
Related
called class:
class LoginViewController: UIViewController {
let chipField: UITextField = {
........
return textField1
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(chipField)
}
}
table to be called:
class MainTableViewController: UITableViewController {
....
}
MainTableViewController call chipfield in LoginViewController
You should really use a delegate in this case, something like
Read more about delegates Here
protocol MainTableViewControllerDelegate {
func getChipFieldValue()
}
class MainTableViewController: UITableViewController {
var delegate: MainTableViewControllerDelegate?
// to get chipFieldValue self.delegate?.getChipFieldValue)
}
In LoginViewController define the function in delegate
class LoginViewController: UIViewController,MainTableViewControllerDelegate {
func getChipFieldValue() ->String {
return chipField.text
}
// later in the code when you present MainTableViewController view pass delegate to self to MainTableViewController object
// something like the MainTableViewControllerObject.delegate = self
}
From your situation all you need is just a property in MainTableViewController
class MainTableViewController: UITableViewController {
var chipFieldCopy:UITextField?
...
}
But I would suggest you rethink if you need the entire UITextField, in most situation you will only need it's text so
class MainTableViewController: UITableViewController {
var chipInfoString:String?
...
}
Is enough.
In your handleLogin() method, you can pass it into MainTableViewController like:
//let navController = UINavigationController(rootViewController: MainTableViewController()) //Replace this line
let mainTableVC = MainTableViewController()
mainTableVC.chipFieldCopy = chipField
let navController = UINavigationController(rootViewController: mainTableVC)
or like I suggest
mainTableVC.chipInfoString = chipField.text
and you can access it in MainTableViewController
Usually, I write this function to init my presenter from viewController. But I want to use init() for making this. What can I do?
ViewController :
class ViewController: UIViewController {
private let viewPresenter = viewPresenter()
override func viewDidLoad() {
self.viewPresenter.attachView(controller: self)
}
#IBAction func faceBookButtonTapped(sender: UIButton) {
self.viewPresenter.showLoginWindow()
}
}
ViewPresenter :
class ViewPresenter {
var controller: ViewController?
func attachView(controller: ViewController) {
self.controller = controller
}
}
So, If I make this Init in my presenter :
let controller: ViewController?
init (controller:ViewController) {
self.controller = controller
}
And try to init like this in viewController:
private let viewPresenter = ViewPresenter(controller: self)
Xcode give me this error:
Cannot convert value of type '(NSObject) -> () -> ViewController' to expected argument type 'ViewController'
the problem is that there is no "self" at that point in time where you want to initialize your presenter because your view controller is initialized later. You could work around this problem by declaring your presenter as lazy like this
fileprivate lazy var viewPresenter: ViewPresenter = {
return ViewPresenter(controller: self)
}()
or you could just initialize your presenter later in viewDidLoad
private var viewPresenter: ViewPresenter!
override func viewDidLoad() {
super.viewDidLoad()
self.viewPresenter = ViewPresenter(controller: self)
}
If you ask me the best approach is (as you already did) to attach the view in viewDidLoad to the presenter.
Hope this helps.
I have 2 different containerView controllers with similar characteristics. So I decide to create a superclass controller in the storyboard and in code to manage these containerView
class ContainerController: UIViewController {
#IBOutlet weak var containerView: UIView!
weak var listController: UIViewController?
weak var detailController: UIViewController?
let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
override func viewDidLoad() {
super.viewDidLoad()
activateListController()
addListController()
}
func activateListController(){}
func addListController(){
self.addChildViewController(listController!)
listController?.view.frame = defineChildSize()
self.containerView.addSubview((listController?.view)!)
listController?.didMove(toParentViewController: self)
}
...
}
So, in the child controller, I need to redefine activateListController() with the specific ViewController
class ContainerViewController: ContainerController, ReactionViewDelegate {
var selectedProduct = String()
var selectedFunction = String()
override func viewDidLoad() {
super.viewDidLoad()
}
override func activateListController(){
self.listController = listController as! ReactionViewController
self.listController = self.storyboard?.instantiateViewController(withIdentifier: "ReactionController") as! ReactionViewController?
self.listController.selectedFunction = self.selectedFunction
self.listController?.selectedProduct = self.selectedProduct
self.listController?.delegate = self
}}
But I have an error with ReactionViewController properties: "value of type viewcontroller has no member selectedFunction". The parent properties is not redifined into a child properties
I also try something like that
class ContainerViewController: ContainerController, ReactionViewDelegate {
var reactionViewController: ReactionViewController?
var mechanismViewController: MechanismViewController?
override func viewDidLoad() {
listController = reactionViewController
detailController = mechanismViewController
super.viewDidLoad()
}
and defining activateListController() with reactionViewController, but I had a nil exception on the method addListController() of the superclass
func addListController(){
self.addChildViewController(listController!) //nil exception
So, how can I well manage inherit with my containerview controller?
Thank
Why don't you do something like this:
override func activateListController(){
let newListController = storyboard?.instantiateViewController(withIdentifier: "ReactionController") as! ReactionViewController?
newListController.selectedFunction = selectedFunction
newListController.selectedProduct = selectedProduct
newListController.delegate = self
listController = newListController
}
I have two classes. One class is named ViewController and the other class is named TabView.
My goal is to call a function changeTab() which is inside the TabView class from the ViewController.
Somehow I am having trouble with it because everytime my delegate is nil.
Here is my code for ViewController:
protocol TabViewProtocol: class {
func changeTab()
}
class ViewController: NSViewController {
// delegate
weak var delegateCustom : TabViewProtocol?
override func viewDidLoad() {
print(delegateCustom) // outputs "nil"
}
buttonClickFunction() {
print(delegateCustom) // outputs "nil"
delegateCustom?.changeTab() // doesn't work
}
}
Here is my code for TabView:
class TabView: NSTabViewController, TabViewProtocol {
let myVC = ViewController()
override func viewDidLoad() {
super.viewDidLoad()
myVC.delegateCustom = self
}
func changeTab() {
print("test succeed")
}
}
Can someone explain me what I am doing wrong? - I am new to delegates and protocols...
You are using the delegate pattern wrongly. It is hard to tell which controller you want to define the protocol for and which one you want to adopt it - but here is one possible way.
// 1. Define your protocol in the same class file as delegate property.
protocol TabViewProtocol: class {
func changeTab()
}
// 2. Define your delegate property
class ViewController: NSViewController {
// delegate
weak var delegateCustom : TabViewProtocol?
override func viewDidLoad() {
// It should be nil as you have not set the delegate yet.
print(delegateCustom) // outputs "nil"
}
func buttonClickFunction() {
print(delegateCustom) // outputs "nil"
delegateCustom?.changeTab() // doesn't work
}
}
// 3. In the class that will use the protocol add it to the class definition statement
class TabView: NSTabViewController, TabViewProtocol {
let myVC = ViewController()
override func viewDidLoad() {
super.viewDidLoad()
myVC.delegateCustom = self
// Should output a value now
print(myVC.delegateCustom) // outputs "self"
}
func changeTab() {
print("test succeed")
}
}
you are creating a new instance in this line:
let myVC = ViewController()
you should get existing instance of your ViewController.then set
myVC.delegateCustom = self
I'm trying to override type for viewControllers manner.
override var viewControllers : [BaseViewController]? {
get {
return self.viewControllers
}
}
But I get this error.
Property 'viewControllers' with type '[BaseViewController]?' cannot override a property with type '[UIViewController]?'
Can this even be done?
You can workaround it by creating new property. Like this for instance.
var baseViewControllers: [BaseViewController]? {
return self.viewControllers as? [BaseViewController]
}
You cannot override properties type which is already of type UIViewController. What you can do is to cast the property viewControllers to [BaseViewController]?.
class base: UIViewController {
}
class tab: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let bases = viewControllers as? [base]
}
override var viewControllers: [UIViewController]? {
get {
return [base()]
}
set {
}
}
}