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
Related
I need to post a notification on viewDidLoad of every one of my ViewControllers. I have a BaseViewController with a postNotification method, and it gets an enum as a parameter to identify the screen. It looks like this
class BaseViewController {
func postNotification(for screen: ScreenName) {
NotificationCenter.default.post(name: notification,
object: nil,
userInfo: ["ScreenName": screen])
}
}
class AViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
postNotification(for: screenA)
}
}
class BViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
postNotification(for: screenB)
}
}
Suppose we need to add another view controller later in the future, such as CViewController. I want to force the developer of CViewController to call this postNotification method with screen enum.
What is the best practice to achieve this on Swift?
Edit
Thanks to Loren's suggestion, I added a protocol on my base class
typealias BaseController = BaseViewController & BaseProtocol
protocol BaseProtocol {
var screenName: ScreenName { get }
}
This forces all my viewcontrollers to conform protocol and initialize screenName, but now I can't get it from my BaseViewController. If I can get child view controller's screenName property from BaseViewController, I would eliminate calling postNotification method on each child, and call it only on BaseViewController
class BaseViewController: UIViewController {
var screenName: ScreenName!
override func viewDidLoad() {
super.viewDidLoad()
if let screenName = self.screenName {
self.postNotification(for: screenName)
} else {
fatalError("screenName must be instantiated")
}
}
func postNotification(for screen: ScreenName) {
NotificationCenter.default.post(name: notification,
object: nil,
userInfo: ["ScreenName": screen])
}
}
class AViewController: BaseViewController {
override func viewDidLoad() {
self.screenName = screenA
super.viewDidLoad()
}
}
class BViewController: BaseViewController {
override func viewDidLoad() {
self.screenName = screenB
super.viewDidLoad()
}
}
Try this since you are using inheritance, have BaseViewController inherit UIViewController superclass, then create a screenName variable. In your childViewControllers instantiate the screenName before calling super.viewDidLoad(). You can use protocols like you mentioned to force the implementation of the variable, but it just seems like overkill for just one variable.
I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.
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)
}()
I'm trying to use the change event on a UISwitch to move from the current ViewController to a new one. My UISwitch is registered in my CustomView for the Custom Cell in my UITableView. The action is registered an calls a class in my View Controller as below
import UIKit
public class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var operatedSwitch: UISwitch!
#IBAction func operatedSwitchChange() {
updateValveOps.valveUpdate()
}
When it gets to my class in my ViewController it calls a method in the main class which should move to my new ViewController as below
import UIKit
class updateValveOps {
class func valveUpdate() {
let valveOps = ValveOperationsController()
valveOps.ValveOpsUpdate()
}
}
class ValveOperationsController: UIViewController {
.
.
func ValveOpsUpdate() {
performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil)
}
However, this causes a Sigabrt error. I've also tried pushing from the current view to the new View Controller but then for some reason it returns back to the calling View Controller! What am I doing wrong?
Try this in your #IBAction func operatedSwitchChange() { .. }
UIApplication.sharedApplication().keyWindow?.visibleViewController()?.performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil)
And add this extension to your project (copy-paste this into new swift file):
public extension UIWindow {
func visibleViewController() -> UIViewController? {
if let rootViewController: UIViewController = self.rootViewController {
return UIWindow.getVisibleViewControllerFrom(rootViewController)
}
return nil
}
class func getVisibleViewControllerFrom(vc:UIViewController?) -> UIViewController? {
if vc == nil {
return nil
}
if let navigationController = vc as? UINavigationController {
return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)
} else if let tabBarController = vc as? UITabBarController {
return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController)
} else {
if let presentedViewController = vc?.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController)
} else {
return vc
}
}
}
}
I managed to find a way to get this working by using Protocol. First I added a Protocol to my ValveOperationsController and referenced it as below
protocol CustomCellDelegator {
func callSegueFromCell()
}
class ValveOperationsController: UIViewController, CustomCellDelegator {
Then I added a delegate to my cell in cellForRowInIndexPath
cell.delegate = self
Then I added the method called in my Protocol in to my ViewController
func callSegueFromCell() {
performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil )
}
Then going to my CustomTableViewCell I added my delegate
var delegate:CustomCellDelegator!
Then in the event called when the Switch changes I added the call to my Protocol
if(self.delegate != nil){ //Just to be safe.
self.delegate.callSegueFromCell()
}
When the event is called when the switch changes it calls the Protocol which passes it to my method and the Segue operates successfully
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