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.
Related
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
}
}
I'm building a weather app that is hosted on a public repo here.
The most important files for this issue are the PageView and the RepresentedPageViewController.
I've created a UIPageViewController that interfaces with SwiftUI via UIViewControllerRepresentable. It allows a user to swipe through different cities and see each city's weather data, much like Apple's Weather app. When the makeUIViewController method of my page view controller is called I set its view controllers (there are 3 in this case to begin with, where each represents a city):
pageViewController.setViewControllers([controllers[0]],
direction: .forward,
animated: false)
This works fine and I'm able to navigate (swipe) between the different pages.
In the app's search menu, a user can then tap on a new city that they want to get the weather for. This adds the new city to the app's datasource of cities that the page view controller uses to create a page for each city.
Because the data object that holds the cities is stateful, the UI is recomputed when that object is set (when a user adds a new city). This triggers the updateUIViewController method in the page view controller. In this method, I reset the page view controller's viewcontrollers (there are now 4 because the user has added a new city):
func updateUIViewController(_ uiViewController: UIPageViewController, context: UIViewControllerRepresentableContext<RepresentedPageViewController>) {
// Check that the view controllers of the page view controller need to be reset.
// This is true when a user has added a new city because we'll create a new
// view controller to be added to the page view controller. We perform this check because
// we don't want to reset the viewcontrollers in all cases where the updateUIViewController method is called.
if shouldResetControllers {
uiViewController.setViewControllers([controllers[0]],
direction: .forward,
animated: false)
shouldResetControllers = false
}
}
My issue is that once this is done, the user is only able to see the first city of the viewcontrollers. The page view controller is still there because the user is still able to swipe, but there is only one city (the first one). I've created a screen recording of the result which you can view here.
think the problem stays in the Coordinator:
when you update controllers in pageviewcontroller, the parent property of Coordinator remains the same (and because it is a struct, all his properties). So you can simply add this line of code in your updateUIViewController method:
context.coordinator.parent = self
also, remember that animation of pageViewController.setViewControllers will occur, so you have to set animated to false, or handle it properly.
There are many other ways to solve this (I wrote the most intuitive solution),
the important thing is to know where the error comes from.
It seems like this is a SwiftUI bug. I had a similar problem where a UI update would reload the PageViewController with a single ViewController.
Adding an id to the PageViewController, described in "Updating an #Environment to pass data to PageViewController in SwiftUI results in loss of swiping between ViewControllers", seems to work.
Throughout my app I use a navigation controller to push and pop my view controllers. When I pop from one ViewController, I check to see if I can reload the data in the previous one with this:
_ = self.navigationController?.popViewController(animated: true)
if let previousViewController = self.navigationController?.viewControllers.last as? AnimalsVC {
previousViewController.tableView.reloadData()
}
This works every time, but now I need to do the same with another view, but it's not apart of the navigation controller because I modally present (instead of pushing it to the navigation controller). Now there's no way I can access the previous ViewController like before and I can not figure out how to access the previous ViewController.
How can I access the previous ViewController to reload the tableview like the example code without accessing the navigation controller?
I know this is possible with notifications, but I prefer not to use that method if possible!
First of all, It's not necessary to access the previous ViewController to reload tableview(or any other func)
I recommend you to use Delegate to achieve the same feature.
Holding a reference to the previous viewController in the way you mentioned will make your app very hard to maintain when your app gets more complicated.
You can call tableview.reloadData() in viewWillAppear method in the controller that you present modally
here is my scenario case.
Initially for going to this VC without loading is hidden.when I click to first view controllers button it goes to second view controller.When I click button from secondVC it come back to first one and for going to this VC without loading button is now visible.Now when I click for going to this VC without loading I want to show my second view controller without reload because my previous loaded data for second view controller is needed.So how can I do that?
the actual scenario of my app look like this.My first VC
and the second one.
It's a picture of sound cloud but the case is same.
First possible solution,
Add SecondViewController as child view controller of FirstViewController using container view in Storyboard.
Every time you want to remove SecondViewController just hide/remove it with custom animation block.
Keep the reference of SecondViewController in FirstViewController
Second possible solution,
Create shared data object.
Then you can use that shared data object in any view controller, regardless of saving the state of any view controller.
I would create an object where i put the data und pass this from ViewController to ViewController by properties. Maybe this is to simple but it should work.
I was unable to find an answer like this as of now but in Swift 3 is there a way to pre-populate a modal view controller with data before opening it? I'm thinking not as I believe the modal view controller won't exist at this point.
Here is what I'm trying to achieve. I have a modal that contains a table view that will be bound with the data to be used to filter the content on the parent controller. The parent controller has to pass the data to the modal that is used in it's table view (I do this by having an init in the modal controller). I have a delegate for going back to the parent from the modal but the content in the modal is always the same so I am looking for a way to set the modal table view with the data so I don't have to rebind it every time it is opened like how the following is done.
func presentModal(){
let modalVC = RoadwaysViewController(roadways: roadways)
modalVC.modalPresentationStyle = .popover
modalVC.preferredContentSize = CGSize(width: 300, height: 350)
modalVC.delegate = self
self.present(modalVC, animated: true)
}
The problem with this is that roadways is always the same, so I would be binding the same data over and over. Is there a way around this?
Give your modally presented view controller a variable that will hold the data,
var data: [MyStruct]?
and set it when you set it's delegate. Or give it something so it knows what data to pull from your data model. Like pass the earliest date you want data from.