How to pass data between View Controllers in UITabBarController? - ios

I have two questions.
I want to pass data. I have two ViewControllers.
Controller A has Data and I want to pass it from A to B controller.
First - tab bar controller has a relationship segue. Can I use this? E.g.: perform segue or prepare?
Second - I want to pass data between ViewControllers in UITabBarController
I can't do this. I searched this I can't find this...
I don't know if the search word is wrong. Can you tell me how to do it or how to search for it?

No. A relationship is not really a segue. No segue is triggered merely by switching tabs.
You just have to put the data where the various view controllers can find it. If you want to notify other view controllers that the data has changed, use a notification or take advantage of the fact that the tab bar controller delegate knows when the user switches tab bar items.

No, You can not pass the data between two ViewController directly, if they are connected to TabbArController.
If you want you can manage through NotificationCenter (So when a new change occur and you post the notification, it will automatically refresh the data)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.getData),
name: .refreshAttendanceForSelectedDate,
object: nil)
#objc func getData() {
/// perform task in view controller
}
// post the changes when you get response ....
NotificationCenter.default.post(name: Constants.Notification.refreshAttendanceForSelectedDate, object: nil)
Or
You can pass the updated data by DataStorgae in a variable of that respective model type (LocalCache) by creating a singleton object. This variable will reside in memory till the app will reside in memory. After forcing kill the app or flush out the app from memory, this variable will automatically be removed.
Note: If needed, please don't forget to set value nil. (Eg. on logout action)
class DataStorage {
/// Created the singleton objectbas
static let instance = DataStorage()
/// Created the properties
var notificationbadgeCount: Int?
var configData: ConfigurationModel?
}
Use by set
DataStorage.instance.selectedStaffId = model.id
get the value
if let id = DataStorage.instance.selectedStaffId {
/// Perform respective task ....
}

Related

(cloud firestore) vc1 has snapshot listener on doc, passes doc data to vc2. vc2 writes data to doc, how to update data on vc2?

My first view controller has a snapshotlistener on a document then passes this doc data onto my second view controller- by let vc = vc2(data:data), the user than navigates to my second vcontroller and has the option to write data to the document.
When the user writes to the document my snapshot listener fires, and the data in vc1 is updated, but obviously the data in vc2 wont be updated.
I could add a function in vc2 to read data from firebase as soon as i call the function to write, but I'm curious if there is a better method of passing the data from vc1 to vc2 so that the data in vc2 is updated when the snapshot listener in vc1 fires?
This decision depends on (1) how many view controllers deep this will go and (2) how you handle these types of situations elsewhere in your app.
To the first point, if there are no other view controllers that need to know this data beyond the second one, then just have the first view controller call a method on the second view controller when data updates. But if there are more view controllers down this navigation stack that need to know about this data update, then I'd consider something else--perhaps a shared resource like a data manager or a view model that all view controllers observe or posting a local notification.
To the second point, just be consistent. However you normally handle these realtime-data situations, try to keep doing that. For instance, if you normally use protocols, use them here.
But for pure simplicity in a scenario where the second view controller is the only other object interested in the data updates in the first view controller, just have the first notify the second:
class VC1: UIViewController {
private var vc2: VC2? // Make it an instance property that the navigation controller pushes to.
Firestore.firestore().document(docPath).addSnapshotListener({ (snapshot, error) in
vc2?.dataDidUpdate() // If the second view controller was never instantiated, the optional chaining of vc2 will fail silently.
})
}
class VC2: UIViewController {
func dataDidUpdate() {
// update UI
}
}

How to update view controller dynamically

I want to ask is there any way to update swift view controller elements(chart, outlets text and background colours). What I mean is I am on the first view controller where I am downloading data from server(JSON) and I am moving the user to the second view controller when the first request to the server finish, but the third view controller doesn't have data already, however the user is able to go on it. There is a loading indicator with indicates if the data is loaded already. I want when the data is loaded this view controller to be refreshed with the data. No matter that user is over it. Is there any way to do this.
When you get the data from the request, find the third view controller in your navigation stack and populate the data.
If the third view controller doesn't exist yet, I suppose you'd have to save the data in the second view controller and wait for the user to go on to the third one and give it the data to populate.
In the callback of your request to the server, it would be something like:
if let navCon = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
if let thirdController = navCon.viewControllers.first(where: { $0 is ThirdViewController }) {
// The third view controller exists, I'll call it and populate the data
(thirdController as! ThirdController).setUpData(data)
} else {
// The third view controller doesn't exist yet. What should I do with the data?
}
}
You also have the option of implementing a delegate or using the notification center.

Where to place the function that passed data between view controllers

I don't know how to make the title specific, I'll explain what I need here.
My question:
This is for a weather app, where I allow the user to change the city they want to check the weather for. The problem is that I created the view controllers programmatically. Meaning the prepareForSegue override function doesn't apply.
Now I did find the answer for what to do instead of the prepareForSegue (bellow is the code), however, where should I be calling the function since it is not an override.
Code:
func prepareForSegue() {
let changeCityVC = ChangeCityViewController()
changeCityVC.delegate = self
present(changeCityVC, animated: true, completion: nil)
}
Thanks,
Your prepareForSegue method doesn't actually prepare for a segue. It simply creates a VC and presents it. So it should really be called presentChangeCityVC or something like that.
As to where to call the method, just call it whenever you want to present your VC! Let's suppose you have a button that says "Change City". You can call the method in the button's target:
func changeCityButtonPressed() {
presentChangeCityVC()
}
Two options jump into my head:
Add a property to the view controller that is going to be presented, something like "titleText". Then when you create that view controller, set the value for that property and use viewDidLoad to assign titleText to the title of the view controller that you are transitioning too.
Create a protocol and add a delegate property on the new view controller of that type. In the first view controller, assign the creator as the delegate. Then in viewDidLoad (or wherever), the new view controller can ask the delegate (the presenter in the case) for whatever info it needs (i.e. the title).

How can I access an IBOutlet in ViewController from another class?

I have a class that is a GCDAsyncUdpSocketDelegate. Now I'm getting some data thru UDP (in a background thread) and need to display that data with an IBOutlet in my ViewController. But ViewController is just a class. Where is its object instance defined? What is its name? What do I do to access it, or one of its properties, methods? I've seen this UIApplication.sharedApplication().keyWindow?.rootViewController method but this doesn't guarantee the exact ViewController I want to access, I guess.
I don't know the exact structure of your app, but you could try something like this:
let rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
if let customViewController = rootViewController as? CustomViewController {
customViewController.label.text = theData
}
else{
let customViewController = storyoboard!.instantiateViewControllerWithIdentifier("CustomViewControllerID")
// (This assumes CustomViewController is defined in the same
// storyboard as the view controller running this code. Otherwise,
// you need to get a reference to the storyboard first).
rootViewController.presentViewController(customViewController, animated:true)
customViewController.label.text = theData
}
EDIT: If the object that receives your asynchronous server data is dettached from your navigation (good call, by the way), you can have it:
Store the data somewhere it will persist even if said object is deallocated
(or make said object a singleton, if applicable).
Post a system notification (using NSNotificationCenter) once the data is available, and have your main ViewController "listen to it" (-addObserver:selector:name:object). When the notification is posted and the notification handler is called, your view controller can retrieve the data from its (persistent) location (file, or property of the singleton mentioned above if you chose that route).
Finally, for the case when the data is already available by the time your ViewController is instantiated, check for data availability and retrieve it if present in e.g. your main view controller's viewDidLoad().
There are many ways you can achieve this,
1. Use Protocols or Delegates
You said "I have a class that is a GCDAsyncUdpSocketDelegate." Write a protocol implementation in that class and make your view controller subscribe to that delegate, when you get delegate call back for your GCDAsyncUdpSocketDelegate do neccessary check and fire your custom delegate to the view controller, pass the data as an argument to the delegate method. In the view controller get the data and update the IBOutlet.
2. Use NSNotificationCenter (dirty way of doing things, gotta be careful though)
In your view controller class add an observer for the notification you are going to post like,
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateUI:", name: "didRecieveDataFromUDP", object: nil)
Add a method in your VC
func updateUI(notifObject:NSNotification){
let responseString = notifObject.object
self.yourOutlet.string = responseString
}
and also remove the notification observer when you are done with the VC.
In the GCDAsyncUdpSocketDelegate delegate class when you get the call back with results fire this notification,
NSNotificationCenter.defaultCenter().postNotificationName("didRecieveDataFromUDP"", object: theResposenObjectYouNeedToSend)
3. Use sharedInstances or mis-use the AppDelegate (not recommended)
Create shared instances or singleton classes that will live through the app lifecycle and use them to store/retrieve data from any class or any thread.
Hope this helps, I wish you choose the first or second way.

iOS UICollectionView navigation

I'm trying to figure out how to navigate around my app. But i'm a little lost.
I have a UIViewController that loads some data, then displays the data in a CollectionView. Then I have another UIViewController for the detailed view. I then trigger a segue to go to it, I pass the data etc.
self.performSegueWithIdentifier("detailViewSeque", sender: nil)
But the part i'm lost on is getting back to my main view, if I just trigger another segue then it loads all the data / view again. The data has already been loaded once, I really don't want to keep loading it.
I feel like I'm doing things wrong, that theres some super obvious way to handle this scenario.
Could someone point me in the right direction?
This is good situation to use an unwind segue (for more information: What are Unwind segues for and how do you use them?). Here's how to setup one up:
Firstly, create an #IBAction in the view controller you want to segue to, that takes a UIStoryboardSegue as its only argument. For example:
#IBAction func unwindToHere(segue: UIStoryboardSegue) {
// If you need you have access to the previous view controller
// through the segue object.
}
Secondly, you need to create the unwind segue in IB. To do this ctrl-drag from the view controller you want to segue from, to Exit and select the unwindToHere method:
Thirdly, you need to give your segue and identifier. To do this select your segue (see below - your segue will not be visible like normal segues); then use the Attribute Editor to give your segue an identifier.
Now you can use your segue. On the view controller you want to segue from, call:
self.performSegueWithIdentifier("YourID", sender: self)
To rephrase your needs "I have data that I need to keep around somewhere that isn't associated with a view controller".
You have a few options here. Your goal is basically to store it somewhere that isn't going to go out of memory.
The AppDelegate gets used for this purpose a lot but Singleton variable works as well.
I would personally create a singleton, say CatPictureRetriever with
private let _CatPictureRetriever SharedInstance = CatPictureRetriever()
class CatPictureRetriever {
static let sharedInstance = CatPictureRetriever()
var catPictures : NSArray?;
func gimmeCatPictures -> NSArray? {
return catPictures
}
}
Now you can get your pictures though your CatPictureRetriever anywhere
var pictures = CatPictureRetriever.sharedInstance.gimmeCatPictures()

Resources