(Multithreading?) Combined with Pushing View Controller - iphone

I am creating a game for iphone that has a few hundred levels. These are all loaded into a scroll view when the user presses the start button so that they can choose a level. However, I need to do a lot of calculations, load each level's data from a keyed encoder, and draw a UIImageView on the scrollview before I can show the screen to the user. As it is right now, this is all done by methods I call from viewDidLoad, so it all happens BEFORE the view controller gets pushed onto the stack. As a result, there is a noticeable delay when the user pushes this button.
I would like to have the view controller be pushes immediately, and then have the levels lay themselves out AFTER the view controller is presented, hopefully providing a better user experience. I imagine this would be possible by calling the methods from ViewDidAppear but I would rather start doing the calculations immediately in order to make things as fast as possible.
Is there a way to do this? Possibly using grand central dispatch or assigning the work to another thread?
Thanks for any help!

If you want the UI content to load first (but not necessarily display) before starting your complex calculations, keep your code in the -viewDidLoad method, but insert it after the [super viewDidLoad] call. If the calculations do not involve objects that must be manipulated only on the main thread (i.e. the UI), use Grand Central Dispatch to set up an asynchronous queue for the calculations to be performed in using dispatch_async().
EDIT: I now understand you're using a UINavigationController to push this view onto the stack and that the calculations all have a direct impact on the UI, meaning that they must take place on the main thread. I would thus recommend placing the code in the -viewWillAppear: method. This way, the system should push the view before the calculations are invoked.
Optionally, in the viewDidLoad: method, you could call another method that performs only the calculations and stores the results in variables on an asynchronous thread. The app could then read these calculations in viewDidLoad:.

There is no magic method to use in the delegate. You are asking a super general question: How to do threading in iOS.

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: Good idea to dealloc and rellocate UI items when switching views?

Suppose I have 2 views. In the first view, I allocate memory to displaying many UI components such as an UILabel, UIImages, etc.
Suppose the user navigates to the next view (via UINavigationController)
Is it OK to deallocate memory assigned to displaying UI components in the first view and then initialize them again once the user goes back to the first view (in viewFirstLoad or the appropriate function)?
It seems to me if you don't do this, then memory will keep on increasing the longer the user uses your app in that particular session.
Is this not allowed? frowned upon? impossible?
It is perfectly normal and in fact, that functionality is built in standard UIViewController - when controller is not displayed its view may be released from memory and you can release all its subviews (e.g. retained through IBOutlet references) in controller's -viewDidUnload method.
When controller needs to display again it reloads its view again.
It depends. Generally, the rule of thumb is that you should free objects that you don't need. If your view is just a view, then yes, I'd release it and all of its subviews. If your view has data that was obtained through a lengthy retrieval process (e.g. a web service call), I'd probably hold onto the data somewhere so that I don't have to go back out and retrieve it when the user goes back to the first view.
To clarify a little: Apple recommends you display data specific to a view in it's -viewDidLoad method, such as setting text on labels. Then, in -viewDidUnload you should release (or nil outlets of) the view objects you setup in -viewDidLoad. It's critical you implement -viewDidLoad, as the base UIViewController code checks that it's subclass actually implements -viewDidLoad before it assumes it can unload the view (and therefore call -viewDidUnload). Failing to implement -viewDidLoad results in the controller thinking it can't recreate your view at a later time, and so it doesn't unload the view from memory. A developer I know experienced this same problem, took forever to track down.

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.

How to Handle an Indefinite Amount of TableViews in an iPhone drill-down app

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.