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.
Related
I have a container UIViewController that hosts a single UINavigationController. The container view controller has a button that opens a new view controller by calling present(newViewController, animated: true, completion: nil).
The newViewController has its own UINavigationController and also contains a button. That button can present another view controller that itself has a UINavigationController and another button and so on.
I want to keep that pattern going for as many iterations as possible and save the states of all of them. Is that possible? To close the current view controller I call _ = navigationController?.popViewController(animated: true) but that erases all the data from the previous view controller as well.
You asked two questions
1) want to keep that pattern going for as many iterations as possible and save the states of all of them.
Ans. : you can not continuously presenting view on already presented view controller. for that first you need to dismiss previous presented view.
2) To close the current view controller I call
_ = navigationController?.popViewController(animated: true)
but that erases all the data from the previous view controller as well.
Ans. If you present any view controller then don't use popviewcontroller but use dismisViewController.
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.
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.
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 have a method that is called when a Settings button is tapped in my root view, that subclasses two UIViewControllers, attaches them to a UITabBarController and pushes the UITabBarController onto a navigation stack:
-(IBAction)onSettings:(id)sender {
// Create the Settings Views
SettingsViewController *vcSettings1 = [[Settings1ViewController alloc] initWithNibName:#"Settings1ViewController" bundle:nil];
Settings2ViewController *vcSettings2 = [[Settings2ViewController alloc] initWithNibName:#"Settings2ViewController" bundle:nil];
// Create the Tab View
UITabBarController *tabController= [[UITabBarController alloc] init];
tabController.viewControllers = #[vcSettings1,vcSettings2];
// Pass the Index of the database on to the views so they can pull the record from the database
vcSettings.recordIndex = recordIndex;
vcSettings2.recordIndex = recordIndex;
// Add the tab bar controller to the navigation stack
[self.navigationController pushViewController:tabController animated:YES];
}
In each of the Settings views, I override the viewWillAppear method to load a row of data from an sqlite database at primary key recordIndex. (Both views pull the same record and display different data from the record, except one field is the same on both.)
I also override viewWillDisappear in each view to save the controls data back to the database.
I can verify that every time I switch views using the tab bar, the viewWillDisappear method is called on one closing view and the viewWillAppear is called on the opening view.
The problem is that when I change data on the first view and switch to the second view, the data is not changed on the second view unless I return to the first view and then back to the second. As best I can tell, here is what seems to be happening:
View 1 is open. I change the data in the field.
I tap on the tab for View 2
viewWillAppear is called for View 2, populating the field in View 2 with the old data in the database.
viewWillDisappear is then called for View 1, saving the changed data to the database.
It seems that the opening view is calling viewWillAppear BEFORE the closing view is calling viewWillDisappear.
I have tried this other ways, such as using a singleton, and simply trying to modify the recordIndex from both views and in all cases it seems that the data is loaded from the opening view before it is saved from the closing view.
Is this a bug in the way UITabBarController works, or am I abusing viewWillAppear and Disappear in a way that I'm not supposed to? Has anyone else run across this behavior?
This is not a good design. There is no guarantee that viewWillAppear of new view should be called after viewWillDisappear from the previous one. Even if that were to work, there would be no guarantee that this would keep working in future iOS versions.
If performance is not impacted, a quick fix could be to save the changes to the database as they occur, you would always have an up to date database that could be accessed from any view in any circumstance.
Best design is to have some model classes, with your model objects accessible through a singleton for example. Those model objects are updated real time as you interact with the UI, they are the ones being accessed by the different views, and they are periodically saved using the method of your choice.
as a cheap solution you could load data in viewDidAppear but in general I agree with JP's answer