Passing information from one storyboard to another - swift

I am having trouble passing the score to quiz app from a model page to one of my controllers. I have a counter that totals up the score, however when I try and add it to a label on another storyboard it takes the original declared value of zero rather than the updated version.
This is where the function of counting is taking place and im just activating the function on another storyboard

Obviously, it will give you zero because there is no update in score variable.
One way to pass score from one view controller to other view controller is as:
create a variable in destination view controller
Initiate destination view controller from current controller.
Assign current score to destination view controller's variable before pushing or presenting it.
In viewdidload or wherever you want to display it, assign score to its label
Here is sample code you can do:
let storyboard = UIStoryboard(name: "myStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "myVCID")
vc.anyVariableYouDeclare = currentScore
self.present(vc, animated: true)

Related

UIPageViewController wont refresh UITableViewControllers

Have a controller, we'll call it Bob, as a modal and it contains some labels and a UIPageViewController that loads 2 table controllers. The first time the page controller loads it loads the table controllers with all the correct data. Whenever Bob is opened again the table controllers data does not change. Bob's labels do change so it is receiving different requests from it's parent.
pageController is the name of the UIPageViewController
I've tried doing the following in the datasource section for the pageController
pageController.dataSource = nil
pageController.dataSource = self
but it didn't work. I've also tried setting the controller again after the new data is loaded and that didn't work either
pageController.setViewControllers([viewController(At: 0)!], direction: .forward, animated: true, completion: nil)
In the table controllers they are set to refresh their data like so and this is called after the new data is passed to them. I've verified that this is called whenever Bob is opened.
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
I've also tried recreating the UIPageViewController altogether and that didn't work either.
I'm really stumped as to how to get the page controller to reload the table controllers with the new data. I don't think the issue is at the table controllers but I'm open to trying anything. Anyone have any ideas?
So the cause wasn't any of what I had listed. Go figure. Instead it was how I was defining the table controllers. I was setting the value they where to use for loading when I was defining the controller and not at the time of when I load it.
So
private lazy var thatTableViewController: ThatTableViewController = {
let storyboard = UIStoryboard(name: "ModalsStoryBoard", bundle: Bundle.main)
var viewController = storyboard.instantiateViewController(withIdentifier: "ThatTableViewController") as! ThatTableViewController
viewController.view.tag = 0
return viewController
}()
used to also have a line like so
viewController.controller = Controller(id: self.markerId, activeController: .Problem)
Controller is a custom struct that I was passing. But by setting this at the same level as where the variable thatTableViewController is defined it would not change between calls since it was cached. So moving the
viewController.controller = Controller(id: self.markerId, activeController: .Problem)
outside the definition part, allowed it get changed and not cached.
This then allowed me to remove
pageController.dataSource = nil
pageController.dataSource = self
since the data is defined at the time that I get the controller that I want to load and not predefined.
Hopefully, this will help someone else to not do the same thing I did and set any values you need the other controllers to use to be defined not when you define the controller but later on when you need to use the controller.

What is the simplest way to modify variable by using the Button for later use of that variable in another View Controller?

I have two view controllers. One VC with a button and a label and one VC only with a label. I created button action that can modify variable (declared in the class of that first viewcontroller) if the button is pressed and I can show that MODIFIED variable as the label in that same VC but my intent is to show that MODIFIED variable on another VC.
I want to use that button to go to the second VC where the modified variable should be displayed on the label when that VC is displayed. In first VC I declared:
var myVariable: String = "default"
In the button action:
{myVariable = "700"}
In second VC I declared:
let vc1: ViewController1 = ViewController1()
I assigned an action to the button in first VC (dragging to second VC to show the second VC) and in viewdidload of the second VC I try to acces the variable from first VC with:
label.text = vc1.myVariable and it accesses it but ...
...but the result is DECLARED/notMODIFIED state of variable. So if I show myVariable on same VC it is OK but on the second VC myVariable was not changed by the button action on the first VC.
I am trying to avoid delegation as it seems complicated..
What is the easiest way to accomplish task? Thank you guys.
To get updated value of first VC variable to second VC you can use NSNotificationCenter.
In first VC when you change value at that time you have to post notification with dictionary like following.
NotificationCenter.default.post(name: NSNotification.Name("ValueUpdate"), object: nil, userInfo: ["variable": myVariable])
For that you have to observe Notification in second VC
NotificationCenter.default.addObserver(self, selector: #selector(handleNotification(notification:)), name: Notification.Name("ValueUpdate"), object: nil)
And then you have to implement following method to get update in your second VC
#objc func handleNotification(notification: Notification) {
label.text = notification.userInfo?["variable"]
}
The SIMPLEST way to do this is to define a global variable. So you can access it from anywhere. It should be define outside of anything to be global.
#import UIKit
var myGlobalVariable = "Initial value"
class MyClass { ... }
then you can change or read it from anywhere like print(myGlobalVariable).
Note that although it is the simplest solution, it is also THE WORST solution too. Global functions and variables use cases are vary rare.
Inject your data via initializer while showing controller.
show(SecondViewController(name: name), sender: nil)
Check out this code.
#Eljer...
Although Delegation seems VERY hard at first its very easy.
You need to remember 3 steps only as a beginner ! I'll try to make it easy for you to use here.
Step 1 : Create tempVariable (of same type) on second View controller also (you can make with same name also doesnt matter)
Step 2 : Write this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let xyz = segue.destination as! abc
}
The xyz is the name you want to call it with.
The abc is the name of your second view controller where you made the other same type of variable.
What this will do is, it will make an object of EVERYTHING (that includes all variables and functions) that you have on your Second View controller available to you on your First view controller also.
To use / assign value to the label on second view controller what you need to do is...
just add :
xyz.yourVariableNameOnSecondViewController = variableNameOnFirstViewController
this will assign the value of your variable on First VC to the variable on Second VC also
Finally, to get the value updated. You need to get this updated value using viewDidLoad on your second view controller.
I hope this helps, if its still confusing ask me anything.
Gluck

Segue and Unwind segue without UI element

I have been looking for an answer for this on SO without success.
I have UIViewController A and B. They are NOT linked in Storyboard, and I want to perform a Segue from A to B, and upon clicking a button in B, to activate an unwindSegue to A.
So far I have :
1) The #IBAction in A to unwind back to
2) The function to prepare the Segue from A to B (In A)
3) The button to call the unwindSegue from B to A
What I'm missing is the logic of the function in 2.
More info about B:
Title: FilterView
Class: FilterViewController
Storyboard ID: FilterView
I tried creating a segue:
let segueToFilter = UIStoryboardSegue(identifier: "SegueToFilterView", source: self, destination: FilterViewController)
Thinking this might just get me what I need.
but I get this error:
Cannot convert value of type 'FilterViewController.Type' to expected argument type UIViewController
Help would be appreciated :)
I still do not understand how segues are causing problems to you, but if you really really don't want to use segues, you can try presenting and dismissing VCs programmatically.
The idea goes like this:
Create an instance of your VC
Pass data to it by setting some properties
Present!
In the VC, dismiss.
The actual code will be different if you embedded your VCs in a navigation controller. Instead of calling present and dismiss, you would call pushVC and popVC on the navigation controller.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "FilterView") as FilterViewController
vc.someProperty = someData // this is where you pass data
vc.present(vc, animated: true, completion: nil)
To pass data back, you should use the delegate pattern by creating a FilterViewDelegate that the first VC conforms to. Look up on Google for more info.

Swift -injecting vars in ViewControllers

I am proudly new to iOS developing and I am trying to build my first app. I am doing a course on an online platform which does the following in the
AppDelegate -> application didFinishLaunchingWithOptions:
let navigationController = window?.rootViewController as! UINavigationController
let notebooksListViewController = navigationController.topViewController as! NotebooksListViewController
notebooksListViewController.dataController = dataController
This app has a Navigation controller which begins with an UIViewController.
I have 2 questions here, first is why this works, I mean, I am in AppDelegate, so the NotebooksListViewController (first view of the app) is not instantiated yet (I think), so why I am able to inject a variable in it?
On the other hand, the second question, is how can I do this in a different scene? I have a TabBarViewController as first scene, and the first tab is a UITableViewController and I want to inject the same way my dataController var, how can I accomplish this? I could not get to do it, neither understand it.
Thanks in advance.
It works, because of some Xcode magic:
In your Target Setting, General tab, the Main Interface entry specifies the name of the Storyboard to be loaded automatically when your app starts up:
In the storyboard, the Initial View Controller then will be instantiated. It seems like this is an UINavigationController.
Since this is done automatically, it just works - until you want to do something special :-)
If you want to start up with a different scene - maybe from a different view controller - you could just change either the Main Interface to another storyboard, the Initial View Controller (inside the storyboard) or both.
Or, you could just start up by yourself, by leaving the Main Interface empty and create your own view controller inside the app delegate (didFinishLaunchingWithOptions), something like
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let tabVC = mainStoryboard.instantiateViewControllerWithIdentifier("TabCtrl") as? UITabBarController {
self.window?.rootViewController = tabVC
// Access the subcontrollers, or create them
// Initialize their values
// tabVC.viewControllers[0].data = ...
} else {
// Ooops
}
self.window?.makeKeyAndVisible()
Answer to your first question
as the method name is self explanatory didFinishLaunchingWithOptions means your application is didfinish with launching with options and its about to enter in foreground so here application need to set rootViewController so in this method controller you want to set as view controller is initiated thats why you can inject variable in it
answer to second question
let navigationController = window?.rootViewController as! UITabbarController
let VC = navigationController.childViewController
//Now Using VC you can access all you controller of tabbar controller
let notebooksListViewController = navigationController.topViewController as!
NotebooksListViewController
notebooksListViewController.dataController = dataController
now as shown above you can use VC to access you view controllers
but be careful here because VC return viewcontroller array so you need make checks for perticular VC you want to access

How to pass map annotation on segue to mapView on another vc?

I currently have a map with annotation and would like to pass this to another map on another VC (replicate the map). How can I do this in my prepare for Segue function?
In my current map VC
if segue.identifier == "addEntrySegue",
let addEntryVC = segue.destination as? AddEntryViewController
{
addEntryVC.mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.05,0.05)
let region = MKCoordinateRegionMake(annotation.coordinate, span)
addEntryVC.mapView.setRegion(region, animated: true)
}
The basic idea of passing data in prepare(for:sender:) is correct.
Obviously, you will want to make sure that the identifier and the cast of the view controller were successful, perhaps by adding a breakpoint inside that if statement. I would have thought that if this if statement was successful, that you would have crashed when you tried to interact with mapView at this point, so I wonder if this if statement is succeeding at all. If it isn't succeeding, you'll want to double check (a) the spelling and capitalization of the storyboard identifier that you specified for the segue in IB; and (b) that the base class of the destination scene is really AddEntryViewController.
But, even if you confirm that the if statement is working properly, you cannot just update UIKit controls of the destination view controller in prepare(for:sender:), because they haven't been created yet. Thus, you can't reference the map view in prepare(for:sender:).
You should have an annotation property in your AddEntryViewController, and the prepare should simply set that. Then, in viewDidLoad of AddEntryViewController, by which point the views have been created, add that annotation to the map view and set the region/camera.