I tried to load a viewcontroller from a custom cell using delegates.But i get nil from the set delegate!
Here is a sample if anyone can help!
1. In Cell
protocol hotelFindDelegate{
func modalDidFinished(modalText: String)
}
class hotelFindCell: UITableViewCell {
var delegate:hotelFindDelegate?
#IBAction func findButton(sender: AnyObject) {
self.delegate!.modalDidFinished("HELLO")
print("Damn nothing works")
}
2. In Main View
class MainViewController:hotelFindDelegate {
let modalView = hotelFindCell()
override func viewDidLoad() {
super.viewDidLoad()
self.modalView?.delegate = self
}
func modalDidFinished(modalText: String){
let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("HotelListVC") as UIViewController
self.presentViewController(viewController, animated: false, completion: nil)
self.modalView.delegate = self
print(modalText)
}
To load view controller from XIB do the following steps.
let settingVC : SettingsViewController = SettingsViewController(nibName :"SettingsViewController",bundle : nil)
later on you can push the same view controller object like
self.navigationController?.pushViewController(settingsVC, animated: true)
NSBundle.mainBundle().loadNibNamed("viewController", owner: self, options: nil)
Related
For example, I have two viewControllers named A and B. I move to "B view controller" after present method is called. I set some values through UserDefaults.stnadard on "B viewController", and I come back to "A view controller" after dismiss method is called. "A viewController" have to show modified values, but it doesn't show the modified ones. I tried calling viewDidAppear() method on "A viewController", but it didn't work. I tried calling viewDidLoad() method, and it worked well, but I heard that it's not a good way to call the method directly. System can only call it. So, I don't want to call it myself. How should I refresh "A view controller"?
From iOS 13.0 and above, Apple has changed the way viewControllers are presented by default. So, viewWillAppear in ViewControllerA will not be called after dimissing ViewControllerB.
One way for making sure the viewWillAppear method will be called after dismissing ViewControllerB is presenting the viewController setting modalPresentationStyle to fullScreen as:
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ViewControllerB") as! ViewControllerB
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
Sample Code:
ViewControllerA.swift
class ViewControllerA: UIViewController {
private var username: String? {
UserDefaults.standard.string(forKey: "USERNAME")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(username) //set this to desired view
}
#IBAction func goToViewControllerB(_ sender: UIButton) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ViewControllerB") as! ViewControllerB
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
}
}
ViewControllerB.swift
class ViewControllerB: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
UserDefaults.standard.set("John", forKey: "USERNAME")
}
#IBAction func dismiss(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
If you don't want to change the modalPresentationStyle to fullScreen then you can use closures for passing data to ViewControllerA when you're dismissing ViewControllerB.
I think NotificationCenter or prototype would help get this done.
NotificationCenter example
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(testFunc), name: NSNotification.Name(rawValue: "PeformAfterPresenting"), object: nil)
}
#objc func testFunc() {
//TODO: your task
}
}
class ViewControllerB: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func dismiss(_ sender: Any) {
NotificationCenter.default.post(name: "PeformAfterPresenting"), object: nil, userInfo: dataDict)
self.dismiss(animated: true, completion: nil)
}
}
A solid way to handle this would be to set up a protocol & delegate, and call it on ViewControllerB's viewWillDisappear.
In ViewControllerB, set up protocol and delegate property:
protocol ViewControllerBDelegate: AnyObject {
func refreshUserDefaults()
}
weak var delegate: ViewControllerBDelegate?
Also in ViewControllerB, call the method in viewWillDisappear
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.delegate?.refreshUserDefaults()
}
Then in ViewControllerA, before you present ViewControllerB, assign ViewControllerA as delegate:
let vc = ViewControllerB()
vc.delegate = self
// present as desired
Also in ViewControllerA, conform to ViewControllerB's protocol:
extension ViewControllerA: ViewControllerBDelegate {
func refreshUserDefaults() {
// do whatever you need here
}
}
The new PopUp ViewController is presented .overCurrentContext
There are 2 buttons with navigation for 2 other views from PopUp view.
A simple action such as print goes well, but when I try to segue programmatically (from xib file, popUpViewController) nothing happens.
let vc = RegisterEmailViewController.instantiate()
vc.coordinator = self
navigationController.pushViewController(vc, animated: false)
Goes well from any other view.
What could be wrong?
PopUpViewController code:
// Created by ᴀʟᴇxᴀɴᴅʀ ᴢʜᴇʟɪᴇᴢɴɪᴀᴋ on 23.12.2019.
// Copyright © 2019 ᴀʟᴇxᴀɴᴅʀ ᴢʜᴇʟɪᴇᴢɴɪᴀᴋ. All rights reserved.
import UIKit
class ViewController: UIViewController, Storyboarded {
weak var coordinator: MyCoordinator?
#IBAction func dismissPopUp(_ sender: Any) {
dismiss(animated: false, completion: nil)
}
#IBAction func buttonEmail(_ sender: Any) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "EmailViewController") as? EmailViewController
self.navigationController?.pushViewController(vc!, animated: true)
}
#IBAction func buttonPhone(_ sender: Any) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "PhoneViewController") as? PhoneViewController
self.navigationController?.pushViewController(vc!, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
There is MyCoordinator which inits all navigations and doesn't let me simply to segue/navigate by my own.
var childCoordinators = [Coordinator]()
var navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
Therefore, a reference to the controller solved the issue. It is possible to navigate aside, until new subViews, separate views are called.
Code that worked in this case:
let vc = RegisterEmailViewController.instantiate()
vc.coordinator = self
navigationController.pushViewController(vc, animated: false)
Conclusion. Simple there are two ways:
To delete all dependencies and other methods which use
UINavigationController
To declare only by new methods for global usage.
I'm having an issue with Delegating. I'm relatively new to the concept, and but conceptually I get it and it's importance. I'm just having trouble using it. I can't seem to pass data from one class to the other. I know there are existing examples of delegation out there on stack overflow but they aren't quite capturing my misunderstanding. I get the use of protocols, delegation, and calling it in a class. I believe there just some small nuance that I'm missing... And it's visible in the lack of functionality in my code
//my protocol:
protocol StingHolder {
func StringPasser(ThisText text: String)
}
Creating the delegate protocol relation, places data to be passed then dismisses the View Controller
// my classes for placing data to be passed
class changeLabel: UIViewController,UITextFieldDelegate{
var Delegate: StingHolder?
#IBOutlet weak var TexrBeingPassed: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
TexrBeingPassed.delegate = self
// Do any additional setup after loading the view.
}
#IBAction func ButtonPassingDataOtherView(_ sender: Any) {
Delegate?.StringPasser(ThisText: TexrBeingPassed.text!)
dismiss(animated: true, completion: nil)
}
}
Creates an instance of the change lable class and its delegate and sets itself to be the delegate *supposedly changes the label, but It doesn't
///class to receive data
class ViewController: UIViewController{
#IBOutlet weak var LableName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
var lableChange = changeLabel()
lableChange.Delegate = self
// Do any additional setup after loading the view.
}
#IBAction func EditController(_ sender: Any) {
var storyBoard = UIStoryboard(name: "Test", bundle: nil)
var ViewController = storyBoard.instantiateViewController(withIdentifier: "TestView")
self.present(ViewController, animated: true, completion: nil)
}
}
inherits the protocol and tells it to change the label to whatever the changelabel class delegate has passes
// extension view controller inheriting the protocol
extension ViewController : StingHolder{
func StringPasser(ThisText text: String){
print("Delegate is working")
LableName.text = text
///
}
}
I want the one view controller to edit the text label of another view controller
The object which you have self as a delegate of, is not the same object presented on the screen.
override func viewDidLoad() {
super.viewDidLoad()
// "labelChange.delegate" is set...
var lableChange = changeLabel()
lableChange.Delegate = self
// Do any additional setup after loading the view.
}
#IBAction func EditController(_ sender: Any) {
var storyBoard = UIStoryboard(name: "Test", bundle: nil)
// but "ViewController" is presented
var ViewController = storyBoard.instantiateViewController(withIdentifier: "TestView")
self.present(ViewController, animated: true, completion: nil)
}
labelChange and ViewController are two different, independent objects. One created by calling init directly, and the other created by calling storyBoard.instantiateViewController. You should set the delegate of the latter instead:
override func viewDidLoad() {
super.viewDidLoad()
// "labelChange.delegate" can be deleted
}
#IBAction func EditController(_ sender: Any) {
var storyBoard = UIStoryboard(name: "Test", bundle: nil)
if let ViewController = storyBoard.instantiateViewController(withIdentifier: "TestView") as? labelChange {
ViewController.delegate = self
self.present(ViewController, animated: true, completion: nil)
}
}
i present a model UINavigationController like so
let flowLayout = UICollectionViewFlowLayout()
let firstViewController = FirstViewController(collectionViewLayout:flowLayout)
let navigationController = UINavigationController(rootViewController: firstViewController)
navigationController.modalPresentationStyle = .fullScreen
present(navigationController, animated: true, completion: nil)
this NavigationController will contain two UIViewcontrollers,
in the last one, when I dismiss the NavigationController, i want to call a function in my main controller before dismissing
I know how to this using protocols and delegates, but only if i use just two UIViewController not a UIViewController and a UINavigationController.
like so
protocol SecondViewControllerDelegate {
func someFunction()
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
#objc func myRightSideBarButtonItemTapped(_ sender:UIBarButtonItem!)
{
self.delegate?.someFunction()
}
}
do I have to create a CustomNavigationController, or is there any other way like passing the delegate through all ViewControllers
You can write something like:
let flowLayout = UICollectionViewFlowLayout()
let firstViewController = FirstViewController(collectionViewLayout: flowLayout)
firstViewController.delegate = self
let navigationController = UINavigationController(rootViewController: firstViewController)
navigationController.modalPresentationStyle = .fullScreen
present(navigationController, animated: true, completion: nil)
or using callback instead of delegate:
class FViewController: UIViewController {
var onButtonAction: (() -> Void)?
#IBAction onButtonTapped(_ sender: Any) {
onButtonAction?()
}
}
class SViewController: UIViewController {
func someMethod() {
let fVC = FViewController()
fVC.onButtonAction = {}
let navigationController = UINavigationController(rootViewController: fVC)
present(navigationController, animated: true, completion: nil)
}
}
When initializing a ViewController via a Storyboard (NS or UI), do you need to keep a reference to the storyboard or can you just ignore it when you are done with it and let it deinit?
class ViewController: NSViewController {
var secondarySB: NSStoryboard? = nil
#IBAction
func loadAndKeep(_ sender: NSButton) {
//keeping a reference
secondarySB = NSStoryboard.init(name: NSStoryboard.Name.init(rawValue: "Secondary"), bundle: nil)
let vc = secondarySB?.instantiateInitialController() as! Secondary
self.view.addSubview(vc.view)
}
#IBAction
func load(_ sender: NSButton) {
//ignoring the storyboard after I get the viewcontroller
let sb = NSStoryboard.init(name: NSStoryboard.Name.init(rawValue: "Secondary"), bundle: nil)
let vc = sb.instantiateInitialController() as! Secondary
self.view.addSubview(vc.view)
}
}
You do not need to keep a reference to the storyboard, but you do need to add the instantiated view controller as a child view controller.