How do I refresh ViewController after dismiss method is called - swift

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
}
}

Related

Set UIImageView From Modal To Another Controller

I have a form the user is filling out, and I present a modal with images for them to choose from. I am attempting to set a UIImage in the original controller from the modal, but the problem is it's nil.
What is the best way to set it? The viewDidLoad doesn't seem to trigger on dismissal of a modal, which is how I'm getting rid of it, and neither does the viewWillAppear. When can I set the image?
My Code right now:
//Collection view choosing deal background
#IBOutlet var collectionView: UICollectionView!
var chosenNumber: Int?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
if(self.restorationIdentifier == "NewDeal") {
if(chosenNumber != nil) {
newDealBackgroundImage.image = UIImage(named: "food_\(chosenNumber!)")
}
}
}
extension BusinessOwnerVC: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
//self.newDealBackgroundImage.image = UIImage(named: "food_\(indexPath.row + 1)")!
chosenNumber = indexPath.row + 1
self.dismiss(animated: true, completion: nil)
}
}
If your presentation style is fullScreen … then your willAppear will be called
navigationController.modalPresentationStyle = .fullScreen
but if you dont want full screen presentation Make yourself the presentation controller's delegate and override presentationControllerDidDismiss(_:).
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("viewWillAppear")
}
#IBAction func show(_ sender: Any) {
let newViewController = NewViewController()
//set delegate of UIAdaptivePresentationControllerDelegate to self
newViewController.presentationController?.delegate = self
present(newViewController, animated: true, completion: nil)
}
}
extension UIViewController: UIAdaptivePresentationControllerDelegate {
public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
if #available(iOS 13, *) {
//Call viewWillAppear only in iOS 13
viewWillAppear(true)
}
}
}
After much experimentation, here's what I found works for me. All I wanted was to access and update a view controller (of the same kind) under a modal I am presenting. Turns out my previous VC was still active, so all I had to do was pass a reference to that VC into the modal, and then I could change its properties.
let modal = UIStoryboard.init(name: "BusinessOwner", bundle: nil).instantiateViewController(withIdentifier: "DealBackgroundSelection") as! BusinessOwnerVC
modal.newDealView = self // This is a weak var of BusinessOwnerVC? declared in the view controller itself
present(modal, animated: true, completion: nil)
And then when I was ready to dismiss that modal:
self.newDealView!.updateDealImage(number: indexPath.row + 1)
updateDealImage was just a function I created to handle all the update. And that works for me.

How can I use a protocol in combination with a navigation controller?

Hey guys I have a little question again. How can I use a protocol in combination with a nav controller. So first of all here are my two classes:
First View Controller:
class VC1: UIViewController{
var cons = "nothing"
#IBAction func PRINT(_ sender: Any) {
print(cons)
}
#IBAction func PRESENT(_ sender: Any) {
let VC = storyboard?.instantiateViewController(identifier: "VC") as! VC2
VC.delegate = self
present(VC, animated: true, completion: nil)
} }
extension VC1: data {
func give(text: String) {
cons = text
} }
Second View Controller:
protocol data {
func give(text: String)}
class VC2: UIViewController {
var delegate: data!
#IBAction func SAVEDISMISS(_ sender: Any) {
delegate.give(text: "Hallo")
dismiss(animated: true, completion: nil)
}}
And here is also my storyboard:
enter image description here
So now I will come to my problem. When I run the code there is an error message when I perform any segue on VC2 because by using the protocol, the navigation controller is excluded, but when i present the navigation controller instead of VC2 the protocol isn´t working anymore.
What could I change so that the navigation controller isn´t excluded and the protocol is working both.
Hopefully anyone can help me, and sorry for my bad english again. I hope you can understand everything.
Have a great day or night.
Replace PRESENT function with the following code when you are trying to present a UINavigationController.
#IBAction func PRESENT(_ sender: Any) {
let VC = storyboard?.instantiateViewController(identifier: "VC") as! VC2
VC.delegate = self
let navigationController = UINavigationController(rootViewController: VC)
present(navigationController, animated: true, completion: nil)
}

Swift NavigationController push method request doesn't work from PopUp view

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.

How do you update the value you delegated from one class to the other?

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)
}
}

How do i load a viewcontroller from a xib file?

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)