Retain View Controller when popped from Navigation Controller using ARC - objective-c

I have two table views and a detail view being managed by a UINavigationController. When a row is selected on the second table, it pops to a detailed view allowing changes specific data represented by the row. I need to be able to switch between the UITableView and the UIViewController without losing any of the data that might have been changed in the UIViewControllers (button selections, text values entered in fields, etc).
Basically, once the UIViewController is presented, one should be able to switch back and forth between the table and detail view without losing any data.
The problem is, when a UIViewController is popped from the navigation controller, that memory is automatically released, and since I'm using ARC, I can't just keep a pointer to that with a retain command in the UITableView.
I know that I could manually rebuild the view each time its corresponding table row is selected, but I feel that might be a messy solution. Another option I've thought of is keeping an array of pushed UIViewControllers and checking if the selected table row corresponds with an existing value before a new one is created, but that might also get messy.
Any suggestions?

Your final wish in this back and forth of view is what you stated in your question: you want to switch between view controllers without losing any information of the actions performed in these controllers.
But if you think at how MVC pattern works, you should consider a view controller as the glue logic between the view and the model. The view is not persistent, that's why it is legitimate for ARC to get rid of the owning view controller when the view is no more needed. Instead what you should persist while your app is working is the model data only: the model data will be shared between the involved view controllers, the view controllers will be recreated each time and the corresponding views will be updated based on the model data. The only reason why the view controller should be kept alive is when its alloc-init-loadView takes too much (e.g.: the view is OpenGL backed) but in such case I would suggest you to keep a strong reference to it in the AppDelegate and ask it to refresh the content when the model data is replaced.
So basically what you should do is:
- select the table
- extract the model data associated to the table, including all information relevant for the view controller
- push the view controller; save all view modifications to the model
- when the view controller is popped, the model data will be returned only
- next time, when you push the view controller again, you will restore the model and re-init the view controller.
This approach is not complicated and gives you the possibility to structure the app in a clean way. Tomorrow you can change your view controller structure (that its view and the logic) without any impact in the communication with the other view controllers as this managed by the model passing only.

There are a couple of solutions to this, just like you suggested.
The array solution is highly inefficient because of memory issues.
The second solution you proposed is a lot more elegant. Just write your own init method in that view controller and init the view controller with data from a plist file

If I'm reading the question correctly, you've got a tableView and a detailView that are driven by the same model data. When changes to the model data are made in the detail view, you want those changes to persist.
If you update the model based on the state of the controls when the detail view is popped, then those changes will persist and the changes will be visible the next time you drill back down into the detail view.
You don't mention what form the table data takes, but let's assume it's an NSArray of NSMutableDictionaries. When you tap the row, the didSelectRowAtIndexPath: method will need to hand the dictionary from that array index to the detail view controller through a property on the detail controller. The detail view controller will update the dictionary values in the method that dismisses it.
The way to think of this is using the model-view-controller pattern. The table and detail view data is stored in the model; the views present the data; and the controllers are responsible for updating the model and navigating between views.

Related

iOS 7 settings like DetailViewController

I have a simple project that was started from a Master/Detail template for iOS7.
I'd like to layout the detail view controller just like iOS settings. Do folks recommend using a table for that or just laying out the controls one by one?
Here is a screenshot of the effect I am looking for:
This is probably a matter of taste/opinion but I prefer tables for this kind of thing for these reasons:
You get all the nice features of tables right out of the box (efficient scrolling, cell reuse and delegate methods to handle where to push new view controllers on to the stack, etc...).
Flexible data model backed cell data. Your table view needs to be backed by some "settings" model object collection, obviously. That collection can be modified to include or exclude settings programmatically. Combine this with custom cells and you're off and rolling. This is really nice if your UI needs to change on the fly.
Code Reuse. If you have another set of "settings" you can use this data-backed table view approach and change only your data model. Doing this manually means you have a new view controller for every settings view. In your example picture, I'd bet my lunch that the 3 view controllers you see in that image are the same type of object.
The table's delegate methods are really helpful when segueing or pushing to new view controllers. Imagine having 10 settings that all took you to separate view controllers. You'd have to manually hook those transitions one by one, yuck.
Of course if you only have 1-2 settings that will never change, perhaps manual is the way to go. For my money, though, tables make sense because things like this always seem to change.

Two views displaying live data at the same time

I have two view controllers which should work like described below:
First view controller displays a view which has a table in it. The table contains data which is constantly changing. I have a delegate method for reloading the data when a change occurs. So that is taken care of. When the user selects a row in the table I would like to display a second view which would also contain live data in text format (one UITextView which would constantly change).
I would like to allow the user to access view 1 while view 2 would still be monitoring and displaying live data and vice versa. While user is on view 2, view 1 should still be monitoring and displaying any changes in the table content.
I guess its like having two view controllers present at the same time and switching between them.
What is the easiest or the most standard way to accomplish this? I dont mind if its all done programatically.
It's a little hard to tell what you're asking here. First, views shouldn't be processing data at all -- they should only be displaying it. It sounds like what you're describing is having a background process that updates to your second view. That process could certainly be running while your table is on screen, and when you switch to the second view, you can update it using this running process. Exactly how you would do this depends on the details of what you're trying to do. So, I think we could help you better if you provide more context on what kind of process you want going on that updates your second view, and how choosing a row in your table affects that process.
After Edit:
You can certainly do what you want to do. I think the main thing is that you need to have a property (typed strong) in your first view controller that points to your second view controller, so that when you go back to the second controller a second time, it goes back to the same instance. Based on the selection from the table, you can start whatever process you need to populate your text view, and that process can keep running even after you go back to the table view, since that controller won't get deallocated when it's view goes off screen (due to the strong reference you have). You would just have to have some sort of if clause in your second view controller to know whether the user has selected the same row again, and if so, just show the updated text view, rather than starting a new process.
That's about as specific as I can be without more detail from you.
what you have is a problem where you need a custom parentviewcontroller
look into splitviews as one example :: SplitView like Facebook app on iPhone
cool intros of how to do custom container view controllers :: Container View Controller Examples

Is there a better way to load View Controllers from a table view?

This might be a very basic question, but I could not find the answer yet.
I have a UITableView that acts as a menu for my app. Each row on the table view, when selected, opens a different subclass of UIViewController.
At the moment my code works the same way used in the UICatlog example from Apple.
In the main view controller (the table view), each menu item is described in a dictionary in an array (menuList). Each dictionary contains an instance of the UIViewController subclass for that screen and other data about the menu item. When the user selects a row, the didSelectRow atIndexPath kicks in and calls the appropriate view controller, stored in the dictionary at that indexPath.row of the menuList array.
It seems to be very wasteful to alloc and init every single view controller when the table view first loads.
My question is: Is there a better way than the one demonstrated in UICatalog to alloc/init my view controller sublasses only when the associated row is tapped?
(I know I can use a complex if..else structure in the didSelectRow, but this results in an extremely long didSelectRow method and breaks encapsulation. I wonder if there is a cleaner way to do this, allocing and initing the appropriate view controller based on data from the dictionaries)
user1349768 try to use Storyboard, but this feature only works in iOs 4 and higher.
Just a suggestion ... put some reference to each view controller into NSArray and then initiate and segue to them when the row gets tapped on (and just get the reference from objectAtIndex:).
Although I could not find a better way to do this, the memory signature of each allocated View Controller is only 288 bytes. Since the solution suggested by apple is a lot more elegant and scalable then using a switch case statement, I left it as it is.

Segue a UItableView to another UITableView

I am unsure about what exactly you must keep separate when you have a multiple MVC project. Say I have two table view controllers, the first one displays a table and when i click on a cell it segues to the second table view controller. The information for the tables is created using methods in a third separate file called FlickrFetcher. The first table View has an instance created of FlickrFetcher allowing it to call the methods. Would I be allowed to import FlickrFetcher into the second table View controller too or am I supposed to keep these controllers separate and only pass the needed information via segue?
In terms of what is allowed by the language, you can certainly have an instance of the FlickrFetcher class in your second table view controller, or when you segue you could pass the reference to the same instance of the class from your first view controller to your second. Otherwise, what would perhaps be better would be to pass only the specific information you need for the second view controller from FlickrFetcher via segue (this depends on how much you need in your second view controller). It's mostly up to you, but passing via segue might be more efficient, and make more sense semantically. Hope this answers your question

Adding a new cell to a Table View from a different view

I was wondering if there is a way i could have a table view displayed on one view then when you hit the "add" button it would flip you to a second view where you would type in the name that you want the new cell's text label to say. Then you would hit a "done" button and it would flip you back to the first view where it would have the newest item. And you would be able to add however many cells you want.
Cocoa and Cocoa touch are based on an MVC pattern. What that means is there's separate layers each with responsibilities - the model, the view and the controller.
The model is the data and the operations on that data, the view is what you see and the controllers mediate between them.
So, in your example, you wouldn't add a cell from a different view. That violates the fundamental pattern. Instead, what would happen is the view where your adding the data would call to the model to add the item and the view would go away. Then, when it returns to the view that lists the items, it would query the model (which contains the new object) and the list would contain the object.
The two views are decoupled and they share the same model. It allows you to change interfaces and storage without breaking most of your app because they're decoupled.
For example, you could have a table view controller that gets it's list from querying your model class. Your model class could be a singleton ( [MyModel sharedInstance] ) that offers a method like:
NSArray* items = [model getItems];
That method could be backed by Sqlite (search for fmdb), CoreData storage, a simple file, or even in memory data like an NSMutableArray. Your UITableViewController implements the callback methods by calling into your model.
Then, you offer an Add button which calls this to modally show your AddItemController.
[self presentModalViewController:addItemController];
After the user supplies the data on the form and clicks the Done/Save button, you call your model to save the item which is a class with the data:
[[MyModel sharedInstance] saveItem:item];
That writes to your storage.
Then, upon return to your UITableViewController, in viewWillAppear, you re query the data and call for the table view to reloadData;
_items = [[MyModel sharedInstance] getItems];
[[self tableView] reloadData];
Now the table shows the data you just added.
There's other variations but that's a basic one with MVC separation.
I'd just like to add, putting this in plain words...
Whatever source that you use for your table to load cells from (an array, dictionary, class, you name it...) then you create the "add" button, make it push the new view controller, then when you press the "done" button on your navigation bar, add the object's property (the name text field info, or whatever you need) to the source you load your table view data from.