The root question is "how many UIViewControllers can you push on the navigation stack?" without causing memory warnings or earning a watchdog termination.
Suppose I have an application that is basically a database for three entities where each can have a relationship with some other entity, and the relationship is shown on a UIViewController. Users can follow those relationships and each one brings up a new controller - If the entities are A, B and C and A->B->C->B->C->A then each kind of view is on the stack twice. I understand how to push and pop, how to push back to a particular controller, and I think rather than just extend the navigation stack indefinitely it might be best to reuse a view controller in the navigation stack.
To do this, every time I wanted a FirstEntityViewController I could scan the navigation stack to find an object where [self isKindOfClass:[FirstEntityViewController class]]; and then call methods designed to rejig that view for what I currently want to see - just refreshing the data in the same way you do when reusing a UITableViewCell.
This is fine except for the effect it might have on the NavigationController. If I use UINavigationController:popToViewController:animated: I think it is going to discard everything above the view I'm popping to, including the view which the user expects to find on tapping "Back" in the navigation bar. So user taps a relationship, taps back and goes "huh?"
If I remove the matching controller from the navigation stack and then pop it on to the top of the stack the back behavior remains OK as long as the user doesn't go back as far as the instance of FirstEntityViewController that was moved or else again navigation will seem inconsistent.
Is the right solution to remove the controller from the stack, and somehow hold a place in the stack so that when the reused controller is popped it can be replaced back where it came from? Should I maintain my own list of view kind and data display so that when popping I can replace the view below the view about to pop to, staying one step ahead of back navigation?
Or is that just getting too complicated? Is there no need to even worry about this situation because the OS reuses much of the view controllers in the same way as UITableViewCells are reused, and there is no real memory or performance impact in having a 50-deep navigation stack?
ViewController instances remain in the UINavigationController's stack, but any view except for the top view may be unloaded at any time (the view controller is notified via the viewDidUnload message).
In other words, the views underneath the top view do not hang around and will eventually be unloaded in low-memory conditions, so there's no need for you to attempt to re-use your view controllers.
Last I checked you can't push a viewcontroller that's already on a navcontroller stack back onto it again. You'll have to create a fresh viewcontroller and push it onto the stack and each back button will pop that one off the stack. The best you can do is make a cache of viewcontrollers and dole them out as-needed -- as long as they're popped off the navcontroller stack. But it probably won't buy you much by way of memory savings.
UITableViews are a bit different in that there's only a relatively small number of cells in view at any given time and as soon as the cell goes offscreen it's removed and returned back into the pool. If you can guarantee that the maxdepth of your chain is fixed, then you can pull a similar windowing scheme. If not, you may have to stick with going deep and be vigilant about releasing memory as soon as you can.
Related
I have a storyboard with 6 View Controllers and their respective views. I do NOT have any navigation controllers associated with these 6 View Controllers.
To go from one view controller to another, the swipe gesture recognizer is used alongside a modal segue. All view controllers have alteast 1, and sometimes even two swipe gesture based modal segues to other view controllers.
My question is, do I need to worry about memory? When I swipe are these viewcontrollers going to be infinitely added to memory eventually causing the app to crash?
An example could be: Say I am in the first VC and I swipe Right to the second VC, then swipe once again to Right to Third VC, then swipe left twice to get back to the First VC will the memory contain this:
Memory: First VC, Second VC, Third VC, Another Copy of Second VC, Another copy of First VC?
I know it seems like a stupid question but since I have only started programming a couple days ago I am very worried I will be having memory issues.
I would appreciate your thoughts and any potentially helpful links to places that dicusses this issue.
you have a good question and it depends on how you are creating and dismissing your view controllers. Normally a design like you mentioned would be built using a built in controller like UINavigationController or UIPageViewController or maybe using a scrollview.
If you are "presenting" new view controllers, then you need to dismiss to remove them. If your not dismissing, then you view controllers will stick around.
If you are using segue's, remember that each segue creates a new instance.
If your logic generally says - swipe right: new modal segue, swipe left: dismiss, then you will be cleaning up as you go. This works if its ok that each right swipe creates a new instance.
If you need the six view controllers to stay in memory all the time, you may want to look at using a pageViewController or roll you own solution. I have seen some nice solutions like you describe using a scroll view.
If you wanted to use a scroll view, basically you would crete an array, load the array with the six instances of your view controllers, then load the scroll view for horizontal scrolling. Add the gesture recognizers and logic for left/right swipes and you have a nice horizontal page scroller.
Here is a very nice reference and tutorial that seems to solve the solution you are describing. http://www.wannabegeek.com/?p=168
good luck and happy new year.
As long as you dont keep strong references to the view (or its subviews, like buttons...), they will be deallocated if needed.
I'm kind of new to iPhone development so bear with me.
I have an application wherein I display a lot of data in a tableviews, edit it in a detailview etc. However, I also have a login-system.
The problem I have is that I can't figure out how to reload the subviews of the NavigationController when I've logged out, or how to dealloc it completely and reinitialize it upon a succesful login.
This means that data from the last user who logged out is still present in my tableview when I log in as another user, as the data is set to reload only when the view loads for the first time.
Thankful for any and all contributions.
There are probably several ways you could go about this; it's up to you (and without more information about your app, I can't suggest a particular solution). You might consider:
-viewWillAppear: — this method is called on any UIViewController subclass when it's about to (re)appear as part of a UINavigationController stack (or tab bar controller, etc.). You can clear out fields, etc. This is mostly useful when a view controller reappears (being uncovered or switched to), because you usually create a new view controller instance each time you display one.
Notifications and delegates — your view controllers (and other objects) can communicate with each other about when a logout occurs, and reconfigure themselves as necessary.
I have a navigation controller which I push a new tableviewcontroller for each received question to be asked to the user, so in stack it builds up to 30 controllers if there has been 30 question is asked.
The difficulty is that in some of the tableviewcontrollers I want to get the input from user by using another navigation controller inside that tableview: for example in the tableview(which is one the many views in the stack) there may be uitablecell with an arrow on the right saying choose the books you want to order, and when he clicks arrow a new tableview will slide in which includes the books to be choosen multiply with a checkmark, and then he will choose and back to main tableview and continue the main navigation there.
How can implement a new navcontroller inside the main navigation controller? or any better ideas?
EDIT: That can be an idea to push the detail view to the same controller, but then they will get popped immdidatly when user "backs" but I want to show the books (which are chosen in detail view) the main table, and when user clicks again to that cell, detailview will come with previous selected books. is this a good approach
Putting an UINavigationController inside a UINavigationController is a bad idea. What you seem to really want is persistence of state data after popping some of the view controllers off your navigation controller's stack.
I would put that state information into a persistent object. If it's a tiny amount of state data I may add that data to an existing object that I know is persistent, such as the app delegate or the root view controller. I would save a more complicated set of persistent data to a custom object. This object would be retained by the root view controller, and a reference would be given to each successive view controller. This object would have the data that may have been collected by a controller that was popped off the navigation stack and therefore no longer exists.
I don't really get why you dont just push the detailview of the uitableviewcontrollers to the main UINavigationController..
Could you explain that a little bit more detailed?
I have no rights to comment your question, so I am afraid I have to post this as an answer.
MfG,
SideSwipe
I'm getting strange navigation bar behaviour, for example when I hit back button the screen displayed is the previous screen, however the Navigation Bar items do change. So I'm left with screen A, but with nav bar buttons for screen B.
Could this be due to memory leaks? I do note with my app still:
This behavior seems to happen:
immediately if I trigger memory
warning via the simulator menu, or
on a device after it has been on
for a while [without being killed
and then restarted as an app].
I do have some memory leaks I'm
trying to clean up (i.e. Profiler
highlights items in "leaked blocks"
section)
Any tips on fault finding root cause of why pushing a back button would end up in a weird state? e.g. screen on previous parent view, but nav bar items don't change...
UPDATE - I have finally removed the memory leaks in my app, however I note the nav bar issue still remains. I guess this doesn't confirm the answer to my question is NO in general, but in my specific case it wasn't the memork leak...
From Apple:
The navigation controller updates the
navigation bar each time the top view
controller changes. Thus, these
changes occur each time a view
controller is pushed onto the stack or
popped from it. When you animate a
push or pop operation, the navigation
controller similarly animates the
change in navigation bar content.
Based on this, I would start by looking for a bug or misconfiguration in your view definitions. Check for any InterfaceBuilder warnings if you defined your views via NIBs. Make sure your view hierarchies are correct in both UIViewControllers. Also check for possible bugs in your view life-cycle methods: viewWillAppear:, viewWillDisappear:, etc,.
Actually, it would be nice if you could post some screenshots and/or code. Thanks!
Any view that is not currently visible and only retained by it's view controller (as a part of the view property of that view controller) will be released (along with any non retained subviews) when a memory warning occurs.
Chances are you are creating the view as a part of init, and not retaining it in the controller (simply letting the view socket hold it from releasing). One way around this is to create properties for the views you create (nonatomic, retain), and after creating them and autoreleasing, assign to those properties, don't forget to assign those properties to nil as a part of dealloc to avoid leaking. Another way is to create your custom view elements in viewDidLoad as opposed to init.
Hard to say without code example from the offending views :)
I've seen something like this happen after calling -[UINavigationController setViewControllers:]. You might try not doing any programatic manipulation of the navigation controller's view controller beyond calling -[UINavigationController pushViewController:animated:].
I've Created a UITableViewController subclass. Do I only need one controller? Do I just instantiate multiple instances of that one subclass?
The iPhone App I'm building will start with a Table of a list of people. If the user taps a person, a new table will be pushed in with a list of Companies they've worked for. If the user then taps a company, they'll see a list of Job Positions. And if they tap a position they'll see a list of people holding those positions.
This could go on forever, and the user could always back up the list.
The App Delegate instantiates the Navigation Controller and the Table View Controller and then pushes it onto the Navigation Controller. But when a user taps a row, now the TVC is creating another TVC.
Is that right or should the
AppDelegate be instantiating all
TVC's? Or does it work out since
they all get pushed onto the Nav
Controller anyway?
Does each Table View instance
need to have a different name or can
they all be called 'mainTVC' or
something like that?
tableViewController *mainTVC = [[tableViewController alloc] init];
Won't I run out of memory? Do i
need to start dropping Table Views
when they're 2 or 3 levels away from
current, and then re-create it if
the user backs up to it?
No need to create multiple TableView's, what I've done in the past is simply re-bind to a different datasource. So keep one TableView and have a datasource for people, then companies, etc...
I'd create a view controller for each type. Presumably you'll want to have special display characteristics like a custom tableview cell to display job positions slightly differently then you would people names.
Other then that, #Ben Gottlieb's answer should work quite well. Use lots of view controllers and handle the didReceiveMemoryWarning: method.
One more thing, if the user drills down so far that you want to say they'll never go all the way back (sort of like having an undo stack) you can use the setViewControllers:animated: UINavigationController method to reset the stack to a certain size (say 15 to implement an 'undo buffer' of 15). With this method you can make sure that the first view controller is always your root view controller and the rest are all drilldown instances.
Option number (2) looks good. You can push quite a lot of view controllers onto the stack before memory becomes an issue. The system will clean up most of the heavyweight memory hogs (ie, the views) in the didReceiveMemoryWarning: method. If you're creating a lot of in-memory structures, you may want to clean them up in that method (be sure to call the inherited method).
To answer your third question, as long as you don't have huge data stores in memory, memory shouldn't be an issue. You SHOULD NOT under any circumstances "drop" tableviews - this would lead to crashes(and there's no way to do non-FILO additions/removals to the navigation stack anyway). Under memory pressure, you should only free "nonessential" items like caches. However, this shouldn't be an issue.
Also, if you have more than 3 or so levels, chances are you need to rethink your UI. If users are drilling down 10 levels, it will be tedious to navigate the stack back.