In my test project I have a ViewController and a TableViewController controller embedded in a Navigation Controller. The ViewController is the main view, and the user can navigate to the TableViewController and then return back to the ViewController.
I am using a 'push' segue when going from ViewContoller>TableViewController, and the TableViewController is dismissed using [self dismissViewControllerAnimated:YES completion:nil]; when the user wishes to go back to ViewController.
In ViewController, I have a button that changes the text on a label:
-(IBAction)onButtonPress:(id)sender {
_myLabel.text = #"New Label Text";
}
When navigating to TableView, and then back to ViewController, the change in the _myLabel.text has been lost and the original text is restored. What is the best way to ensure UI data is retained when navigating between views? I might only have one label in this project, but at some point I will have many UI elements, for example, WebViews that need to keep a page loaded when the user navigates away and then comes back.
What would you suggest is the best method to implementing this?
Thanks for your time.
First of all, View is your data representation, any data is your Model. So you may store Model with its entities and values. Your controllers manage data to represent it on View or to change Model according to user actions in View.
So add some Model-object for storing _myLabel.text. And use this object for both controllers in your app.
Try to remove setting the text from the ViewWillAppear or ViewDidAppear method, put it in ViewDidLoad.
Related
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 an tabbed application that captures data, stores it using NSUserDefaults and presents a readout in a table that is contained in a UIViewController. I am using a navigation controller between the tab view controller and the data view. The data view is then connected to a history view controller which is a child view controller. The history also has a table.
When you press a row in the history view, the app is supposed to transition back to the data view and present the readout for the history. This is done by making the data the first NSUserDefault which is then presented in the data view. THe transition I am using is
[self.navigationController popToRootViewControllerAnimated:YES];
This transitions back to the data view successfully however it does not change the data in the view. However,when I remove that code and press the row and then press back, the readout is changed. How do I make the readout change on press of the row?
Try to use delegates/protocol,
Set your dataview as delegate to the protocol in history view. Before calling pop, call the delegate action. Then in the action, just refresh ur view in dataview and then call poptoroot.
Just google protocol/delegate example, there should be many.
Delegates / Protocols. The answers are abundant here for passing data between view controllers.
passing-data-between-view-controllers
All that I ended up having to do was implement
[self.dataTable reloadData]
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
I have a TableView which describes a book with sections which represents the chapters and rows representing the verses.
A the top of this TableView I have a button in a navigation bar to allow "navigation".
The goal of this navigation button is to allow the user to easily jump to a given chapter/verse without scrolling manually (which can be very long).
When the button is pressed a tableview controller is called displaying all the available chapters of the book and when a chapter is selected another table view is called displaying a list of the available verses in the current chapter. Finally when the line is chosen the tablew view displaying the book should scroll to the given index/row.
So the idea : from the tableview representing the book I call the chapters view as modal and the verses as a push over the chapters view.
My problem is that I don't get the point of managing the delegate and dismissing from the 2nd modal view.
With 1 modal view I do things like that.
In the displayed VC (View Controller) I added the protocol and the delegate
#protocol ChapitresTableViewControllerDelegate <NSObject>
- (void)didDismissPresentedViewController;
#end
#interface ChapitresTableViewController : UITableViewController
#property (nonatomic, weak) id <ChapitresTableViewControllerDelegate> delegate;
#end
I have in the didSelectRow
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate didDismissPresentedViewController];
}
in the displaying VC I add the following line
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
ChapitresTableViewController *chapitresTableViewController = segue.destinationViewController;
chapitresTableViewController.delegate = self;
}
and of course
-(void)didDismissPresentedViewController
{
[self dismissViewControllerAnimated:YES completion:nil];
}
this would just work fine if I wanted to close after the first modal VC, but what I want is to have the second one being dismissed after I click in the second. Ok I can write the protocol and stuff in the second modal VC but how do I manage to have the delegate being send to the second VC.
Hope my question is clear enough it is not so easy to explain it.
Anyone understand me and can help me ?
NOTE : I know for now I don't pass any parameters back to the delegate, which I will do later to do the scroll. For now I just want to be able to close the second view, then I will add the required stuff to pass the parameters back to the delegate
I'm sure you can do this, but rather than modal view controllers with a navigation bar, wouldn't it be easier to use a navigation controller? Then you can use popToViewController to go back as many levels as you want to a particular view controller. You can either pass the UIViewController* of the various controllers you might want to pop to, or do so programmatically: e.g. How to pop back to specify viewController from navigationController(viewControllers/stack)?
In this scenario previous views controllers are retained. The ones you pop off are released (just like the modal ones you dismiss are released), but the ones that you pushed from are retained (just like the ones you presented from in a modal world are retained).
If the book is large, though, you'll have to be sensitive to memory usage. Thus, you will probably want to handle didReceiveMemoryWarning to release the model data for the previous views in either your modal sequence or push sequence, in which case, on viewDidAppear, you'll want to see if your app had to release the memory in response to didReceiveMemoryWarning and reload it in that case. But that's the desired behavior, either way, gracefully release the pages if needed (and reload them when the particular view reappears), but keep it in memory if you can.
Finally, you might also want to contemplate using UIPageViewController. Given what you've described, I'd like consider UIPageViewController first, UINavigationController and push segues second, and the use of modal segues third.
I have a simple iPhone app based on a navigation controller. There are two view controllers, VC1 and VC2, both with table views. VC2 also has a custom table cell. When a user selects a row in VC1, VC2 is pushed on to the stack. When the user selects the back button it's removed. Typical navigation stuff.
The problem I have is that the data in the cells in VC2 persists when the back button is pressed, so that when the user selects a different row in VC1, VC2 is pushed back on to the stack with the 'old' data in the cells, before the methods in VC2 reload the data.
I want to make sure that the data in the table in VC is removed every time the back button is pressed. I've tried releasing the tableview using viewWillDisappear, but it's not working. What's the recommended way of dealing with this situation? I've looked at the docs but it's not obvious (to me at least).
Try out this code snippet in viewWillDissapear or dealloc method.
if(yourTableViewCellObject) [yourTableViewCellObject release];
if(yourTableViewCellObject) yourTableViewCellObject=nil;
This might work.
I've used this technique several times since I asked the original question. As I mentioned in my comment to #Aditya, I've found that the easiest way to deal with this is to use viewWillDisappear to hide the tableview with the 'old' data, and when the user navigates back to the page, wait until the 'new' data is loaded into the table before making the tableview visible again.