Good design for having an ActivityIndicator across entire app - iphone

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.

Related

How to speed up slow loading of segue to table view in iphone app

I have a welcome screen which leads to a table view.
When users tap on a row they are taken via a push segue to a detail view. This loads quickly.
There is also another push segue possible to another table view but this is slow loading.
I think this is because it contains a search bar and data which have to be loaded.
Is there any way I can instantiate this table view when the program loads rather than wait for the segue? The class for the slow loading table view is called BSGlossaryController.
I was thinking something along the lines of myGlossaryController = [[BSGlossaryController alloc]init] but am not sure.
If you want the BSGlossaryController to instantiate at startup, you should delete the segue to it, instantiate it at startup, and push to it manually. You don't want to use alloc init to instantiate it though, that will create a different one than the one in your storyboard. You want to use the UIStoryboard method instantiateViewControllerWithIdentifier: to instantiate it (and make sure you give it an identifier in IB). When it comes time to push to it, you'll want to hook your button (or whatever you're using to trigger the push) to a method that uses pushViewController:animated: to do the push.
If you do this at startup (in the app delegate), then you'll have to assign the newly created BSGlossaryController to a property, and access that in which ever controller you're going to push from. If you can wait to instantiate it until that controller (the one you're pushing from) is on screen, then you can create it in that controller's viewDidLoad method.
Typically, you don't want to load one giant chunk of data at a time. If the chunk is large enough to significantly slow down your view loading, it's a good bet that you need to rethink your approach. Memory issues aside, loading large amounts of data at one time will block the UI of your app as it's loading. This will leave the app unresponsive and the user will wonder what is going on.
You can handle loading large data sets in a few different ways -- presented in order of preference:
Use an NSFetchedResultsController to load your data. This will automatically take care of caching and chunking the data so responsiveness and performance are maintained.
Break your data into smaller subsets. The screen can only display so much data at a time, so it's pointless to load data into memory that isn't currently being presented. Bring in new data as the view requires it.
First present the view (without data), display an activity indicator with a message informing the user that the data load is happening, then bring in the data asynchronously on a background thread. This approach will at least not leave the user wondering what is going on.
Once you post some code and I have a better idea about what you are trying to do and why, I can give more specific answers.

When to put into viewWillAppear and when to put into viewDidLoad?

I get used to put either of viewWillAppear and viewDidLoad, it's OK until know. However I'm thinking there should be some rules that guide when to put into viewWillAppear and when to put into viewDidLoad?
Simple rule I use is this. viewDidLoad is when the view's resources are loaded. The view is not drawn onscreen yet. So calculations and code dealing with the geometry and visuals of the view should not be put here. They should be in the viewWillAppear or viewDidAppear method.
Also viewWillAppear can be called multiple times
When a popover/modal view is displayed and remove
When an alert view/actionsheet/uiactivityController's view is displayed and removed.
For these reason, viewWillAppear should not contain codes that takes longer to finish. (at least the code running on the main thread). Neither should codes that only needs to be run once per view display.
There are more I am sure but these are simple to remember and I hope it helps.
viewDidLoad: Alerts you that a view has finished loading
viewWillAppear: Runs just before the view loads
viewDidLoad is things you have to do once. viewWillAppear gets called every time the view appears. You should do things that you only have to do once in viewDidLoad - like setting your UILabel texts. However, you may want to modify a specific part of the view every time the user gets to view it, e.g. the iPod application scrolls the lyrics back to the top every time you go to the "Now Playing" view.
However, when you are loading things from a server, you also have to think about latency. If you pack all of your network communication into viewDidLoad or viewWillAppear, they will be executed before the user gets to see the view - possibly resulting a short freeze of your app. It may be good idea to first show the user an unpopulated view with an activity indicator of some sort. When you are done with your networking, which may take a second or two (or may even fail - who knows?), you can populate the view with your data. Good examples on how this could be done can be seen in various twitter clients. For example, when you view the author detail page in Twitterrific, the view only says "Loading..." until the network queries have completed.

(iphone) pushViewController in background thread?

IPhone SDK - Leaking Memory with performSelectorInBackground
seems to suggest you can actually pushViewController in background thread.
But I get a warning from stackoverflow people that I shouldn't do this.
eg. (iphone) start timer from background thread?
is pushViewController considered "UI updating"?
(because I know that UI updating should be done in main thread only)
If it is so,
when a viewController takes a while to load, and want to show an indicator while loading.
What is my option?
Couple of strategies here.
1) BEFORE you do the push, but at the point you know you are going to do it, bring up a suitable activity view on the current view. I do this in some apps where you click on a row in a table but the pushed view has to do some web comms that takes time, so I leave the table cell highlighted in blue and add a white spinner to the left of the disclosure indicator. Works well.
2) Use lazy loading to get the new view controller on screen quickly, but defer the heavy code until after it has loaded so that the new controller can look after it's own activity view. By lazy loading I mean you should do as little as possible in the init method and make careful use of viewdidload / viewwillappear / viewdidappear etc to spread the work out and get an activity view on screen as soon as you can.

iOs: When should I initialize upcoming controllers (UIImagePickerController) from a view?

Suppose I have a navigation controller where the next action is to take a picture or select an image from the library.
If I initialize UIImagePickerController during didSelectRowAtIndexPath:, (I believe) the response will be a little slower as the controller needs to initialized. Also, if the user cancels and opens again, it would reinstantiate that controller every time.
However, if I create the controller during viewDidLoad: of the navigation controller, it takes up memory while the user is on that view. Side questions: Does this, however, slow down the loading time of the navigation?
Or should it be done in an NSOperation when the view is loaded?
Overall, what would be the best place to load the ImagePicker?
I'd lazy load the controller when it first gets called (in didSelectRowAtIndexPath) so it wouldn't have to be reloaded every time, and not worry about initialization time.
It seems that in your case you will always need an image picker on didSelectRowAtIndexPath. You can load it on the view and customize (camera, cameraroll, etc.) and present on the row selection. Probably it doesn't matter that much. UIImagePickerController presentation is very slow anyway, especially with the camera.
I don't recommend an NSOperation for this task.

Best way to load an application like it was in its previous state when it was terminated

I would like to learn the best practices in reloading the application state, such that when my app is started, it should automatically load the "correct" view/subview when it is opened again.
In my particular case, my app has a bunch of view controllers, each taking care of a UITableView. I would like for my app to "jump" to the correct node within my table view hierarchy when it is opened again.
Building on what Marc said, assuming you've got a base view controller, and then one or more levels of 'drill-down', load all your view controllers up to the 'current' one using [navigationController pushViewController: viewController animated: NO]. Then, when the user hits the Back button, they'll be presented with the pre-loaded previous view controller. A good example of this is the "Contacts" app, which preloads the Groups view controller, then pushes a view controller for the current group (usually "All Contacts") on top of that.
Check Apple's DrillDownSave sample app:
"Demonstrates how to restore the user's
current location in a drill-down list
style user interface and restore that
location when the app is relaunched."
http://developer.apple.com/iphone/library/samplecode/DrillDownSave
You'll need to associate an identifier with each view controller, and save that value to the user defaults when the current view changes. The next time you launch, read that value and you'll know which view controller you need to load initially.
I've published a little library to help doing these kinds of things. I'm already using it in an app I've published on the App Store and it's a lot smoother than implementing NSCoder for every view manually :P