I have an iPhone application, and I have included a virtual "timeout" for being in the background. When it enters the background, I make a timestamp. When it re-enters, i compare the current time to the timestamp. This all works great.
What I want is for the application to basically reset like it was just launched. Everything in my application lives inside of a UINavigationController, so I thought I could just release it and everything inside, then reallocate it and start over. Is there a right way to do this? I have a feeling that if i just "release" the UINavigationController, all of the ViewControllers inside will just leak into memory.
You can just release your UINavigationController, and if your ViewControllers are only retained by your UINavigationController, which should be the case, then they will also get deallocated.
You may reset your model data manually and then return to start screen of your app by
[self.navigationController popToRootViewControllerAnimated:NO];
To do this in a sensible way you need to reset your controllers and data model to the state you want (e.g. as if your app was just launched). I'd also do this in a way that looks natural for your users. Something like the following:
Load a splash screen view (or something temporary) in the main app window.
Release your navigation controller (more generally, release the top level controller owning the view previously added to the window). this will release all associated controllers and views assuming you haven't retained them anywhere else.
You may also need to handle any modal views that were displayed when your app went into the background. You can either dismiss them automatically when the app goes into the background or keep state around to dismiss them when the app resumes and you are resetting it).
Reset your data model to the state you want
Recreate your navigation controller and add it back to the window and release the temporary splash screen.
Related
My apps "short" description:
Basically an interactive storybook, I have a class that sets up a audio session and audio player which every other class(ViewControllers) in my app imports and calls a function or two to set the right sound to be played each time something happens(for instance.. user reads the story). Each ViewController has it's own .m and .h classes and uses them for animations and action handling. My app is only about 60 mb's in size (audio/images/code).
Now these ViewControllers are set up in a storyboard (they are 13 now) and are modal segued from one to the next one and then programmatically dismissed to go back.
When I run my app on my iPad now, I'm starting to get memory warnings and yes Instruments is showing me that my app is adding roughly about 40 Mb's for every ViewController that I segue to.
My questions are:
Do they reside in real memory no matter what I do? (I thought I wasn't holding any strong pointers to these view controllers).
Is there an easy way for me to dismiss one controller and still use a modal segue to get to the next one?(ran into troubles trying this)
Modal Segues are probably not the way I should be doing things in my App are they?!. They looked so nice and easy for my "storybook", but now they are giving me a very rough time.
Any other tips you can give me from what I described are appreciated.
Thank you.
Yes, as long as you present it modally. The presenter view controller keeps a strong pointer to the presented view controller. What you could do here is in the viewWillDisappear: release all the images and other views that might use memory !
You could instantiate your view controller using the method instantiateViewControllerWithIdentifier: and then presentViewController:animated:completion: like you would do with any view controller
It's up to you to decide. But you could easily mimic the animation if you wanted to.
If I understood everything correctly I would go with a singleton class kinda like 'AudioEngine' which is accessible from anywhere in any class. Then I would design all my viewcontrollers in my storyboard like you did. When I need to present modally another view controller, I'll do it using the answer of your 2nd question. If I still had a memory issue, I would try to cheat and keeps always 3 view controllers (like we do with the UIScrollView's infinite scroll) that I would reuse and I'll mimic the modal presentation using UIView's animation blocks.
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 the need for an activity indicator view in my app when different views are loading and when data is being retrieved. The problem is the mainVC (where I would place the indicator) is not always aware of when processing is happening so it can start the indicator but it cannot stop it.
e.g. the mainVC loads and then programatically adds a new VC - this VC in turn asks a model to retrieve - it displays data etc. So this newly added VC actually knows when processing is finished and it does not have access to the indicator view (although the indicator is visible at the top).
I was thinking of using notifications - is this the best way of handling this situation?
I'd recommend looking at the brilliant MBProgressHUD library:
https://github.com/matej/MBProgressHUD
It's a very simple set of classes you can use to display loading and progress views that can be accessed by all view controllers in your app. Basically, you can set it up in your app delegate and add it to your app window.
Every view controller can then access the progress view from the delegate and show/hide it when required. It comes with an example project and code - it's very easy to use and customise.
Notifications are one half of the solution. You have to combine them with a persistent object so that you can also get the current state at all times. E.g., when a view controller is about to appear, it needs to read the initial "downloading" state from somewhere, because the VC might have been created after the "start" or "end" notification was sent.
Then, while the VC is alive, it can simply respond to notifications to update the indicator.
This design is particulary important for views, which run the risk of getting unloaded/reloaded all the time.
I'm trying to figure out what logic should go into the different UIViewController methods like viewDidLoad, viewDidAppear, viewWillAppear, ...
The structure of my app is that I have a root view controller that doesn't really have a view of its own, rather it has a tab view controller and loads other view controllers into it. But in the future, it may have to load other view controllers instead of the tab bar controller depending on app logic.
My main question is, what do people usually put into the viewDidLoad, .... methods.
Currently I:
viewDidLoad - setup the tab bar controller and set its view to the view controller's own view
viewDidAppear - check if user has stored login info
if not - present with login
if so, login and get app data for first tab
I'm trying to figure out now if my logic for setting up my tab bar controller should go into loadView rather than viewDidLoad.
Any help would be great. Small examples found on the web are great, but they don't go into detail on how larger apps should be structured.
You should not implement both -viewDidLoad and -loadView; they are for different purposes. If you load a NIB, you should implement -viewDidLoad to perform any functions that need to be done after loading the NIB. Wiring up the tabbar is appropriate there if you haven't already done it in the NIB.
-loadView should be implemented if you do not use a NIB, and should construct the view.
-viewWillAppear is called immediately before you come onscreen. This is a good place to set up notification observations, update your data based on model classes that have changed since you were last on screen, and otherwise get your act together before the user sees you. You should not perform any animations here. You're not on the screen; you can't animate. I see a lot of animation glitches due to this mistake. It kind of works, but it looks weird.
-viewDidAppear is called after you come onscreen. This is where you do any entry animations (sliding up a modal, for instance; not that you should do that very often, but I was just looking at some code that did).
-viewWillDisappear is called right before you go offscreen. This is where you can do any leaving animations (including unselecting tableview cells and the like).
-viewDidDisappar is called after you're offscreen (and the animations have finished). Tear down any observations here, free up memory if possible, go to sleep as best you can.
I touch on setting up and tearing down observations here. I go into that in more depth in View controllers and notifications.
viewDidLoad will be called once per lifetime of each UIViewController's view. You put stuff in there that needs to be set up and working before the user starts interacting with the view.
viewDidAppear is called whenever the view has appeared to the user. It could potentially be called more than once. An example would be the root screen of an app using a UINavigationController to push and pop a hierarchy of views. Put stuff in there that you'd want done every time. For example, you might want to hide the UINavigationBar of the root screen, but show it for all subscreens, so you'd do the hiding of the bar here.
Therefore, you'd put your logic for setting up your UITabBarController in viewDidLoad, since it only should be done once.
Regarding your app, is there a reason why you don't just make the UITabViewController be the controller loaded by your app delegate? It seems that you have a level of indirection in your app that you may or may not need. It's probably better to simplify it now, and refactor later if you need something more complex.
I have created an iPhone app where you start in a NavController and after a couple of levels you select an option from the table and an animated view pops in that has a tabbarcontroller at its root.
This is a completely seperate view that replaces the navcontroller. You tab around and when you want to go back to the options screen you press back and another animated transition plays swapping back the navigation controller removing the tab bar controller and releasing it.
The problem comes with releasing the UITabBarController. If you press the tab buttons to switch between tabs it seems you continually increase the reference count for the view controllers. Then when you call release on the tabBarController reference it will only release the view controller of the selected tab or any tabs you haven't viewed yet, and it takes one of the retain count on the others tabs viewcontrollers. This means you leak all your Model objects, custom cell objects etc from each of the other tabs that were not selected.
As an example of what I am seeing, if you create a new template TabBarController project in xcode and add a viewWillAppear method to the first view controller that prints out the retain count for itself. Start the app and press back and forth on the First and Second tab buttons and watch the log the retain count just keeps increasing.
So I am wondering if there is a way to release a tab bar controller and have it release all of its view controllers at the same time?
If this is true, it's a bug, and you should file a report on Apple Radar.
Just for completeness, I did post this problem in the apple dev forums looking for some confirmation but never heard anything back.
Checking out my app on OS3.0 shows that this behaviour is now fixed. Reference counts never go increase and releasing the UITabBarController appears to work.
Sadly I put a nasty hack in to fix this which doesn't play well with OS 3.0 so it's now conditional compilation time.