Access Variables of ViewController Before Popping To It - ios

I have a home button which triggers this code when pressed.
I need to change a variable within the viewController I am popping to before I pop to it. Is there any way to do this?
func goHome(){
let switchViewController = self.navigationController?.viewControllers[1] as! UIViewController
self.navigationController?.popToViewController(switchViewController, animated: true)
}
This is how I thought you would go about it but no variables of the viewController appear in the autocomplete window.
switchViewController.x = 5
Any information on how to go about this and/or why this isn't working as is would be greatly appreciated.

You're setting switchViewController as a generic UIViewController, which has no variable .x.
You should set it as the correct class, which in this case would be whatever you named the class that has the 'x' variable:
let switchViewController = self.navigationController?.viewControllers[1] as! SwitchViewController
switchViewController.x = 5
In this case I've used the class name SwitchViewController, which means you'd need a .Swift class file like so:
import UIKit
class SwitchViewController: UIViewController {
var x: Int!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

What type of UIViewController your switchViewController is?
In order to have custom variables in the the UIViewController, you would need to subclass it, and when getting it back, you would do it like this:
func goHome(){
let switchViewController = self.navigationController?.viewControllers[1] as! YourViewController
self.navigationController?.popToViewController(switchViewController, animated: true)
}
Taking x as the example variable, UIViewController has no variable of this type, but your subclass (which i expect you have one) of UIViewController might have, and then auto-complete would kick in.

Related

Delegate/Protocols Passing data from one view controller to another

Trying to pass data from one view controller MainScreenVC to Another RatesVC with protocol and extension, but that's not working, app crashing everytime . I'm clearly see that problem with code on second VC(because print showing correct data after action on first VC) but not sure where is error.
StoryBoard and 1st VC Example
Second VC
1st View controller
import UIKit
protocol transferNameOfCurrency {
func currencySelected(nameOfCurrency: String)
}
class MainScreenVC: UIViewController {
var transferCurrencyDelegate: transferNameOfCurrency?
var nameOfTheCurrency: String?
#IBAction func updateRates(_ sender: Any) {
nameOfTheCurrency = "EUR"
transferCurrencyDelegate?.currencySelected(nameOfCurrency:
nameOfTheCurrency)
print(nameOfTheCurrency)
}
}
2nd ViewController
import UIKit
class RatesVC: UIViewController {
var currencySelected: String?
override func viewDidLoad() {
super.viewDidLoad()
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC
{
push.transferCurrencyDelegate = self
}
// Do any additional setup after loading the view.
}
}
extension RatesVC: transferNameOfCurrency {
func currencySelected(nameOfCurrency: String) {
currencySelected = nameOfCurrency
print(currencySelected)
}
}
The most obvious problem lies here:
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC {
push.transferCurrencyDelegate = self
}
You have to realize that instantiateViewController creates a new view controller - it's not the reference to the view controller presented at the screen. In that code you just created a completely new view controller and then set its delegate to self, but otherwise nothing else.
Without knowing the context it is really hard to suggest anything - prepare(for:) segue might be the place where you want to set the delegate. Anyway, the problem is that you have to obtain a reference to the controller that is presented on the screen, the one that is supposed to be reacting to those events.
Moreover, from the memory management aspect, you should really consider making the delegate property a weak one to prevent memory leaks.
EDIT
So after seeing the minimal working example you provided at link, I think I can provide the solution on how to get that string to the SecondVC.
Your first view controller with comments:
import UIKit
class ViewController: UIViewController {
var newLine: String = "EUR"
#IBAction func push(_ sender: Any) {
// here the secondVC does not exist yet, calling delegate.transferWord() here would have no sense
// performSegue will create that secondVC, but now it does not exist, nor it is set up as the delegate
self.performSegue(withIdentifier: "ViewController", sender: navigationController)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? SecondVC, segue.identifier == "ViewController" {
// at this moment secondVC did not load its view yet, trying to access it would cause crash
// because transferWord tries to set label.text directly, we need to make sure that label
// is already set (for experiment you can try comment out next line)
secondVC.loadViewIfNeeded()
// but here secondVC exist, so lets call transferWord on it
secondVC.transferWord(word: newLine)
}
}
}
No need for delegates here, because your ViewController is the one pushing the SecondVC to the Navigation controller - that means that you can access it directly in prepare(for:), as you can see above.
Now the SecondVC is super simple (I omitted unnecessary code):
import UIKit
class SecondVC: UIViewController {
#IBOutlet weak var label: UILabel!
func transferWord(word: String) {
label.text = word
}
}
Storyboards can stay as they are.

Error passing data between two ViewControllers when declaring the delegate within a UITabBarController

I’m trying to pass data between two ViewControllers with the initial call being made from a UITabBarController.
Here is what I’m doing. I’m using a class called RaisedTabBarController to add a custom button to a TabBarController, which works fine displaying the button, my issue is that when I tap the custom button I want it to take me to FirstViewController and then I want to pass data from FirstViewController to SecondViewController via protocols but for some reason I’m getting an error that in my opinion doesn’t make any sense, it complains about a labels not being accessible within SecondViewController.
Here is the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is the code…
Class ref from GitHub:
RaisedTabBarController
TabBarController
Here I'm adding the custom button and making the call to go to FirstViewController
import UIKit
/// TabBarController subclasses RaisedTabBarController
class TabBarController: RaisedTabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Insert empty tab item at center index. In this case we have 5 tabs.
self.insertEmptyTabItem("", atIndex: 2)
// Raise the center button with image
let img = UIImage(named: “myImage”)
self.addRaisedButton(img, highlightImage: nil, offset: -10.0)
}
// Handler for raised button
override func onRaisedButton(_ sender: UIButton!) {
super.onRaisedButton(sender)
// Go to FirstViewController
let pvc = storyboard?.instantiateViewController(withIdentifier: “firstStoryBoardID”) as! FirstViewController
/// Here, I’m not sure if this is the right way to tell that
/// SecondViewController will be the delegate not TabBarController, seem to work
pvc.delegate = SecondViewController() as FirstViewControllerDelegate
self.present(pvc, animated:true, completion:nil)
}
}
FirstViewController
From here I want to send data to SecondViewController
protocol FirstViewControllerDelegate {
func messageData(greeting: String)
}
class FirstViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
}
func sendData() {
self.delegate?.messageData(greeting: “Hello SecondViewController”)
}
}
SecondViewController
Here I want to receive the data sent from FirstViewController
class SecondViewController: UIViewController, FirstViewControllerDelegate{
#IBOutlet weak var labelMessage: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func messageData(greeting: String) {
/// I do get message from FirstViewController
print(" Message received from FirstViewController: \(greeting)")
/// Here I get error, fatal error: unexpectedly found nil while unwrapping an Optional value
/// I think it has something to do with the labelMessage not being accessible, but why?
labelMessage.text = greeting
}
}
Any idea why am I getting the error in SecondViewController, why wouldn't labels be accessible if they are declared in SecondViewController?
Ideally I would like to be able to call method onRaisedButton(_ sender: UIButton!) directly from SecondViewController but without having to subclass RaisedTabBarController. I’m not usr if this would solve the error but I think this would make my code cleaner.
EDIT: 06/19/2017 - Solved
The effect I was looking for can be done directly in XCode, in the storyboards. I stopped using the third party class (RaisedTabBarController), problem solved.
This seems wrong.
pvc.delegate = SecondViewController() as FirstViewControllerDelegate
Try to instantiate the SecondViewController like you did for the first from storyboard.
let svc = storyboard?.instantiateViewController(withIdentifier: “secondStoryBoardID”) as! SecondViewController
And then set the delegate to SecondViewController
pvc.delegate = svc

BannerView Nil when calling function from another class

I'm getting nil when unwrapping an optional value with GADBannerView..
I setup my ad banner like this, in FlashViewController.swift..
class FlashViewController: UIViewController {
#IBOutlet weak var bannerView: GADBannerView!
and then in ViewDidLoad:
func initAdMobBanner() {
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716"
bannerView.rootViewController = self
bannerView.load(GADRequest())
}
bannerView has an outlet in storyboard to Root View Controller, which is class FlashViewController.
Then in TableViewController.swift I have my purchase button. Purchase button runs:
FlashViewController().HideMyBanner();
The function HideMyBanner is in FlashViewController and will run this code:
if bannerView != nil {
print("bannerview Contains a value!")
bannerView.isHidden = true
} else {
print("bannerview Doesn’t contain a value.")
}
The issue is, if I create a button directly in FlashViewContorller.swift and run the same function, bannerView contains a value and can be hidden.. If I call the function from TableViewController.swift, it returns nil, (or crashes if I try to hide bannerView... I feel like I missing something easy here, but already spent a long time trying to figure it out...
By using this line FlashViewController().HideMyBanner(); you are creating new object of FlashViewController. so it will crash.you need to use the object of FlashViewController which is already created and loaded in memory.
I think you need to pass the reference of FlashViewContorller to TableViewController
If your TableViewController is load from FlashViewContorller than you need to create reference FlashViewContorller in TableViewController like this way.
class TableViewController: UIViewController {
var objFlashViewContorller : FlashViewContorller?
override func viewDidLoad() {
super.viewDidLoad()
//This is the UILabel
}
func purchasebuttonClick() {
objFlashViewContorller?.HideMyBanner();
}
}
While setup Navigation FlashViewContorller to TableViewController you need to pass reference.
let tableViewController = self.storyboard!.instantiateViewController(withIdentifier: "TableViewController") as! TableViewController
tableViewController.objFlashViewContorller = self
self.navigationController?.pushViewController(tableViewController, animated: true)
OR
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "TableViewController" {
let vc = segue.destinationViewController as! TableViewController
vc.objFlashViewContorller = self
}
Don't use FlashViewController().HideMyBanner();
I think you need to use this in in TableViewController.swift -> my purchase button method.
self.revealViewController.frontViewController.HideMyBanner()
I used Notifications to finally work this out... Controller 2 sends a notification that a purchase has been made, and Controller 1 observes and waits for this notification, then takes care of hiding the banner in Controller 1.
https://blog.bobthedeveloper.io/pass-data-with-nsnotification-in-swift-3-73743723c84b

Swift - Passing value back to ViewController using protocol and delegate

I'm new to swift and my problem right now is I want to pass the value from RoutePreviewController page back to display on ViewController page after clicking on the Navigate Button. As below picture, you can see that the RoutePreviewController is not directly segue from the ViewController.
This is my story board of the swift project on xcode
However, I tried using protocol and delegate in the code.
Here are codes that I have add to the ViewController (MainPage)
protocol HandleSelectedPath{
func selectedPathDraw(selectedRoute:[(line:Int?,node:Int,time:Double)])
}
in viewDidLoad of ViewController
let routePreviewPage = storyboard!.instantiateViewControllerWithIdentifier("RoutePreviewController") as! RoutePreviewController
routePreviewPage.handleSelectedPathDelegate = self
and an extension outside the ViewController class
extension ViewController : HandleSelectedPath{
func selectedPathDraw(selectedRoute:[(line:Int?,node:Int,time:Double)]){
print("7687980")
selectedPath = selectedRoute
routeDraw()
}
}
And in RoutePreviewController, I have delegation of the protocol.
var handleSelectedPathDelegate: HandleSelectedPath? = nil
and the Navigate button action
#IBAction func navigateButtonAction(sender: AnyObject) {
handleSelectedPathDelegate?.selectedPathDraw(previewPath)
dismissViewControllerAnimated(true, completion: nil)
definesPresentationContext = true
}
As a result, after clicking the Navigate button, it does send me back to the ViewController page but the selectedPathDraw function of the protocol is not performed. I also tried printing some random string but nothing came up as an output.
The reference for your RoutePreviewController according to your code above only exist inside your viewDidLoad, you have to set as property instead like this:
var routePreviewPage: RoutePreviewController!
Always it's a good practice implement your delegate as a weak reference to avoid retain-cycles, so the correct way of implement your delegate and protocol should be as the following code:
protocol HandleSelectedPath: class {
func selectedPathDraw(selectedRoute:[(line:Int?,node:Int,time:Double)])
}
RoutePreviewController:
weak var handleSelectedPathDelegate: HandleSelectedPath?
#IBAction func navigateButtonAction(sender: AnyObject) {
handleSelectedPathDelegate?.selectedPathDraw(previewPath)
dismissViewControllerAnimated(true, completion: nil)
definesPresentationContext = true
}
viewDidLoad of ViewController:
routePreviewPage = storyboard!.instantiateViewControllerWithIdentifier("RoutePreviewController") as! RoutePreviewController
routePreviewPage.handleSelectedPathDelegate = self
I hope this help you.

How to fix ViewController does not conform to ViewControllerDelegate

I have a CoreData project where I've got a FilterViewController set up and I want to display the results in a DisplayResultsViewController.
I know it's something to do with the delegate, but I'm stumped how to fix it. I know I'm not the first person to move data from one VC to another, but I can't figure out the answer.
// Top of FilterViewController
import UIKit
import CoreData
protocol FilterViewControllerDelegate: class {
func filterViewController(filter: FilterViewController,
didSelectPredicate predicate:NSPredicate?,
sortDescriptor:NSSortDescriptor?)
}
// Predicates set here
// IBAction to trigger the segue
#IBAction func filter(sender: UIBarButtonItem) {
delegate!.filterViewController(self,
didSelectPredicate: selectedPredicate,
sortDescriptor: selectedSortDescriptor)
dismissViewControllerAnimated(true, completion:nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toDisplaySearchResults" {
let navController = segue.destinationViewController as UINavigationController
let filterVC = navController.topViewController as StretchSelectorViewController
filterVC.coreDataStack = coreDataStack
filterVC.delegate = self // **ERROR here
}
}
You haven't shown the code that declares the class containing the method that fails (and thus determines what self is) so we're missing the key line of code. But in general, your view controller should be declared something like:
class MyViewController: UIViewController, ViewControllerDelegate {
....
}
... where the first class after the colon (UIViewController) indicates the superclass and the following subsequent items(s) identify protocols to which your class is declaring conformance. The error indicates that your class doesn't conform to the ViewControllerDelegate protocol, so it must be missing that declaration.

Resources