How to avoid UI freezes? - iphone

Im trying to find the best way to avoid having tiny UI lockups after certain events in my app. For example here are two cases where i have come across mini-lockups:
I have a button that when pressed loads a local mp3 file (around 20-30mb) using AVAudioPlayer, calls the prepareToPlay method and then plays the audio file. The issue is that the button has the showsTouchWhenHighlighted flag set to yes and after pressing it it stays highlighted until the audio file begins playing which could take 1-3 secs.
Another similar example is using a navbar button to sort and reload the table data. There is a very short but noticeable lockup where the button remains highlighted until the table has reloaded.
Can anyone suggest a good approach to minimizing/eliminating these mini lockups? I didnt really come across any code to achieve this in any of the books/tutorials i read that dealt with tableviews and avaudioplayer. Should i be launching these actions on different threads?
thx

For number 1, your best bet is to load the item on a background thread and inform the user that you're doing so (either via a loading HUD or some other indicator). The simplest way of doing this will be to use - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;. If you're running iOS 4.0 + you may want to look into executing block callbacks and see if they will work for you.
For number 2, perform the sorting on the background thread or change your sorting method (consider using a sorted data structure rather than resorting after inserts?). reloadData should occur only on the main thread.

I'm an iPhone noob myself, but it sounds like your code is stuck doing synchronous actions, which will guarantee that your UI gets locked up until the action is done executing.
Although I don't have an explicit answer, look for asynchronous options to perform these actions as they will not lockup your UI. These are usually achieved through using threads or deferred objects. From my experience so far w/ Objective-C, most actions your program neds to take can be achieved through async actions.

Related

run a process in the background while user can still use the UI

I am attempting to run a database fetch process in the background without locking the user interface.
Currently I have a button that does this, but I would like it to be automatic so that it can get more results as user is browsing current results.
Here is the code that the button does, I would like to make this automatic and not lock the UI. Also if there is a way to pause the process, but continue where it left off if user goes to another screen that would also be very useful.
Thanks in advance!
-(IBAction)continueUpdatingResultsButtonPressed:(UIButton*)sender{
[findMoreButton removeFromSuperview];
[self continueFindingMoreRecipes]; //(do this in background without locking screen)
[self loadRefreshButton];//At completion load this button (a blinking button) to refresh the cells with new results
}
A typical pattern you can use is something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// perform data processing here (done in the background)
dispatch_async(dispatch_get_main_queue(), ^{
// update user interface here (done on the main thread)
});
});
You could do batch requests where you cache the next X amount of answers every time your UI got with in Y of the current end. Depending on what you are using a lot of databases have protocols that can help you batch cache easily. Good luck!
Use grand central dispatch. Create a new queue, dispatch it with a block and when you need to update, call dispatch get main queue. There is no way to pause this once the queue has been dispatched though. Maybe load recipes into intermediary then update as needed.
Look for some gcd tutorials there are a few decent ones there.
Would give you more code but I'm typing on iPhone.
It strikes me (especially since you are, by your own admission, "very new to programming") that it might not be prudent to pursue GCD to prefetch data in a background queue, make sure you coordinate the background queue's database operations with the foreground's database operations (possibly via something like FMDB's FMDatabaseQueue or something equivalent), gracefully handle pausing this operation as you go to other screens (as you indicated in your question) and making sure you don't cause retain cycles in the process.
To make matters worse, I gather that this is all in pursuit of a possibly questionable goal, if I understand you correctly, to retrieve 10,000 recipes in the background. Don't get me wrong. You certainly can design all of the background operations like we've outlined, but I think you should stop and ask yourself whether that's the right design for your business problem.
I might suggest an infinitely easier solution. Just load your tableview with the first x recipes, and as the user scrolls down, when you start to approach the end of the tableview, detect that fact and retrieve the next x records and add them to the tableview. But any user interface that is expecting the user to flip through 10,000 entries doesn't pass the smell test. When I think of a database with 10,000 entries, I think of something more like a imdb/wikipedia/google/facebook-like user interface rather than a contacts-style user interface.
Regardless, you almost certainly don't want your app just endlessly retrieving recipes in the background. You can solve your UI performance issue, but maybe replace it with memory management issues. And you're going to design a complicated system architecture when it's not entirely clear whether your recipe app requires that.

Adding a lot of data to core data while keeping the UI responsive

I'm looking for a way to add a whole load of data to core data while keeping the little activity indicator spinning on the UI. I tried adding the data on another thread but since learned that core data is not thread safe, and I get all kinds of errors. Can anyone suggest another approach to this?
Thanks in advance.
Core Data is not thread-safe, but that just means you have to code appropriately, rather avoiding them entirely. To keep your UI responsive/up-to-date, you'll need to use threads.
Apple's documentation on the subject is here, and this blog post is an excellent walk-through of using multiple threads with Core Data, and some of the pitfalls involved.
You need to use a separate managed object context for each thread. There's some additional work you need to do to make changes from other contexts available to your main thread's context: see Concurrency with Core Data for a full discussion.
Most useful example is a core data XML downloading from iOS sample code. If u don't have access, please let me know and I will write here is a main structure of it.
There is a complex custom delegate techniques from u UIviewController to nsoperation delegate, this is important for u to start showing content immediately to user and show a progress of sync.
In AppStore u can see my "snow IXC" app, where u can see this techniques implemented for indicate user in their UIviewController about progress. It's free for downloading.
This is how I avoid background threads with a loading indicator (I use DSActivityView but this should work with other implementations):
In your code when you are going to be displaying the indicator run all the code to show the indicator first. After that code have a separate method call to do all the loading work. Call it by using the method:
[self performSelector:#selector(loadMethodName) withObject:nil afterDelay:0.0];
Normally the app will go straight into the loading code without waiting to show the indicator view. By calling it this way it will first finish displaying the indicator before it moves into the loading code.

Iphone memory warning pause application

I am writing an application for a 2g device and I am receiving memory errors. The errors are created by saving a large file to the device while searching sql libraries and displaying popups (picker view mostly).
I know this is a bit vague but I was wondering if there was a way to pause user interaction while letting the device complete processing it's data. I have tried using sleep(), and [NSThread sleepForTimeInterval: 0.5], but this all pauses the application also.
Preferably this would be called in didReceiveMemoryWarning to let the app catch up processing data while blocking user interaction.
Thanks
One option is to add an invisible (i.e. transparent) view on top of all the others. Ideally, you reduce its alpha value to make it sort of gray out the underlying user interface and display a spinner or something like that to indicate that the device is working.

Is it possible to get multiple MPMoviePlayerControllers in the same view?

I have written code so that technically there should be two MPMoviePlayerControllers within the same view, both ready to play an audio file. What always happens is that one pops up and disappears, and the other comes up and stays, and is fully useable.
Any idea why one disappears? I'm not trying to play two at once, just want to give users the option to play from different sources.
There can only be one active MPMoviePlayerController, it won't mind if the user didn't play it yet. I do not know the technical reason, but I do know that this is the normal behavior when you do that.
My advice would be to use AVAudioPlayer to achieve what you want. The sad part if that you will have to implement the UI of the player.

NSTimer Lag - iPhone SDK

I made a game that uses many timers throughout the code. However the timer has to deal with many tasks in such a small amount of time which leads to the problem where there is lag in my game. For example, my timer runs at an interval of (0.05) and it needs to draw and update many of the images on the screen. Is there any way I can distribute the work flow so that the program runs much smoother?
Thanks
Kevin
I would use an NSThread instead of an NSTimer. I have had more success in this area using NSThread because it runs on an independant thread and is not fired off your main ui thread. In the loop for the thread sleep it for 1/20 (your 0.05) of a second. Because the thread is not running on the UI thread all of its tasks should not slow your UI down. However beacsue it is not running on the UI you will have to call performSelectorOnMainThread to get the UI to update from this background thread. I put a lock on my update method (a simple boolean) that says if the last ui update has not happened, just skip this one. then if im running out of processing time i just drop a frame or two here and there. I also do a lot of checking to see if anything has actually changed before i redraw.
Simple solution: Ditch NSTimer.
Move your redrawing code to a single method, then use CADisplayLink. The issue with using your NSTimer approach is that everything is being redrawn too fast or too slow for the screen. By using CADisplayLink, you can synchronize your redraw code to the screen refresh rate. All you need to do then is touch up your code so that it can deal with not being called at a specific time.
And yes, check to make sure you don't need to redraw as Aran Mulholland said above. Just make sure the checks don't take as long as a redraw.
And remember to optimize your code. A lot. Use ivars to access objects, but the whole property (self.myObject =) to set your objects.