When using a UIActivityIndicatorView, it is possible to add this view to your view hierarchy and start animating it.
After that you are completely save to block the main thread (not that this is a good approach), there is basically nothing that I know of, that could stop the activity indicator from spinning.
My question is:
How is this done? It seems like UIActivityIndicatorView uses its own thread for rendering.
Is this something that can be achieved with my own views?
I have a CoreAnimation animation that I want to keep playing while the main thread might be blocking for a couple of milliseconds.
How to do that? Thanks for any help or ideas!
//EDIT: To clarify my question: I want to know what Apple does to get UIActivityIndicatorView animating even when you block the main thread. When I trigger my own CoreAnimation and I block the main thread, the animation itself stops. Furthermore to the question what Apple does to achieve that under the hood, I want to know, if I can achieve this myself, with public API.
You best solution is to use a background thread for long processing while keeping the main thread only for display purpose. That way, you will be able to display your view without blocking and run your long precessing at the same time.
You can use Grand Central Dispatch (GCD) dispatch_async to run your long processing code on another thread and then call dispatch_async on the main thread to refresh your UI.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
// Your long processing code
dispatch_async(dispatch_get_main_queue(),
^{
// Refresh UI here
});
});
Related
Consider the following case:
Main Thread -----UIView info \ --------------------------------------- / Button Updated ------
\ (Some Event) / (Some Event)
\ /
BG Thread ---------------------Validate UIView info-----Update Button------------------------
On the main thread, a UIView is present
UIView makes a dispatch call is made to a background thread
In the BG Thread, the UIView's info is validated.
BG Thread makes a dispatch call to a UIButton on the main thread to update it.
My question is - notice how there is a gap between UIView info and the UIButton which means that the app could technically be updated during this time. How can I possible stop this gap? Essentially, from the BG thread, block the Main Thread till the call comes back?
You cannot and must never block the main thread. If you do, the interface will freeze and, if the blockage lasts too long, the WatchDog process will kill your app dead before the user's very eyes.
You can give the user a sense that something is going on, and discourage the user from doing anything, by, say, turning off user interaction and putting up a spinner. But in general, yes, multithreading is hard; you must be prepared for the possibility that you will come back onto the main thread when the app has changed state.
Rather than block a main thread, disable user input controls in your view until the validation is complete, then re-enable them.
It would also make sense to add an activity indicator with hidesWhenStopped set to true; it will show the user that there's background work in progress if you start it when the background work starts, and stop it when validation is complete.
If there's ever a chance the background process could hang or take longer, e.g. if it's making a network request, you might show/enable a cancel button and a way to terminate it.
Showing activity indicator and possibly providing a cancel button both require that the main thread keep running, so definitely don't block it!
Your button should not be updated in the background. You should always modify UIKit components on the main thread.
You should also never block the main thread, what you're looking for is the show the user an indication that a background process is active. 'UIActivityIndicatorView' might be a good thing to show the user, you could also disable user interaction on the view to prevent the user from touching anything if it's critical for them to wait until the operation is complete but not recommended.
Yes, you should never block the main thread and update UI only on main thread.
That said - show a spinner / activity indicator while busy on the background.
Think carefully about the UI and e.g. present something so the user can not change something while you are busy with e.g. dialog or popover or something like that.
In practise this often becomes more a question of UX than blocking.
This is perhaps more existential than a concrete question, but I'm struggling with a bit of a user experience issue in my app.
In my app, I have a task that converts a UIView to an UIImage, does some off-screen processing, and updates the UI for the user. All of this happens on the Main Queue, as is required for such UIKit tasks.
I want to have an activity indicator in my app (I'm using a custom designed one, but a regular UIActivityIndicator demonstrates the same issue), which I also have running on the Main Queue, prior to the aforementioned task.
My issue is that once the UIView processing kicks in, my activity indicator freezes until the task completes. This is obviously due to the main queue handling another, more intensive task, hereby hanging the spinner. My curiosity is; how can I ensure the spinner continues, regardless of the UI work happening on the main queue?
Thanks!
I'm afraid this is impossible unless you do the heavyweight operation on the background thread.
You can try to give the main thread a little bit air to breathe by chunking the operation to smaller parts, if that can be done. That would at least allow some updates of the spinner.
While I think that you should keep taking an image and updating the UI on the main thread, considering putting processing the image at the background thread, if that is possible.
I agree with Milan. I'd suggest a slightly different flow:
Start your activity indicator spinning.
Grab your view and convert it to an image.
Pass the image processing to a GCD background queue for processing, and pass in a completion handler.
When the background processing is complete, invoke the completion handler on the main thread. In the body of the completion handler, stop/hide the activity indicator.
I would suggest something like this.
-(void)render {
startSpinner();
backgroundRenderQueue = dispatch_queue_create("backgroundQueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(backgroundQueue, ^{
//Render image here
UIImage *image = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageview.image = image;
stopeSpinner()
});
});
}
I have used the
dispatch_sync(kBgQueue, ^{
});
to get response from webservice at background. While getting the response at background the UI getting delay, ie) while typing the on uitextview the keyboard getting freeze at moment.
How to fix this issue?
Thanks in advance..
dispatch_sync will wait for the block to be executed (which is going to take time depending upon the operation). This blocks the main thread where the UI is updated.
In order to get a responsive UI you need to replace dispatch_sync with dispatch_async.
right code:
dispatch_async(kBgQueue, ^{ });
dispatch_sync Submits a block object for execution on a dispatch queue and waits until that block completes.So it will block your main thread.
for move, see this
I have an architecture that takes input from the mic and then performs some calculations and then should render to screen.
The issue is that calling setNeedsDisplay never triggers a call to drawRect because the main thread is running the calculations.
What would be the best way to thread this?
Create a single serial queue and dispatch the work to this background queue using GCD and dispatch the final setNeedsDisplay back to the main queue, or is there a more efficient way of doing this?
keep everything long running off the main thread. that is best in my opinion. Long running calculations are also something
try to never block the UIThread
...work to this background queue using GCD and dispatch the final
setNeedsDisplay back to the main queue, or is there a more efficient
way of doing this?
Based on your pretty vague description, I'd say what you suggested is the best approach.
And if you do this with GCD, your code will be easy to read since the blocks of work you perform are inline in your code making it easy to understand what is going on.
Create a single serial queue and dispatch the work to this background queue using GCD and dispatch the final setNeedsDisplay back to the main queue, or is there a more efficient way of doing this?
You should use the highest level API available to you. In this case, it's NSOperation or one of its subclasses. Probably, NSBlockOperation is the one you want. You should
create a block operation with the block you want executing
Set its completion handler to invoke setNeedsDisplay
Stick it on an NSOperationQueue.
The completion handler needs to invoke setNeedsDisplay on the main thread. You can do this by sending performSelectorOnMainThread:withObject:waitUntilDone: to the view e.g.
[myViewThatNeedsUpdating performSelectorOnMainThread: #selector(setNeedsDisplay)
withObject: nil
waitUntilDone: NO];
I think this is a better approach than using GCD directly because it is more in the Objective-C idiom, it separates the work you are doing from the notification at the end and gives you many more options for how things get done than raw GCD. For instance, if you want several of these things to happen sequentially, you can make some operations dependencies of others without having to write lots of code.
A UIViewController takes about half a second to load its contents and appear on screen. How can I make them all load in the background and appear when they're ready?
There is a LazyTableImages sample on the Apple developer site.
It shows how to perform the heavy lifting in a background thread and update the UI on the main thread.
PerformSelectorInBackground:withObject: is a possible solution, although a more modern method would be to use asynchronous blocks. You can run code on the main thread from within these blocks to update the UI Safely.
The Concurrency Programming Guide is a good place to find more information and examples of this.
A Background Thread cant update the UI,you can perform all the processing logic in background thread and call the main thread for UI update
Example to load a tableView with Data ,use the background thread to process everything and load the Data, call [tableView reloadData] using the main thread, see Grand central Dispatching to know how to Work with Threads in IOS..
Hope it Helps
Create a GCD queue to process your work in a background thread (read the docs, because my "create" label and options may not be what you want).
You send it to the queue asynchronously, meaning that the call to dispatch_async will make appropriate arrangements for the block of code you give it to run in another thread and it will then return back to you "immediately."
All the work in the block you give it will be executed on a separate thread. Note, at the end, you do another call, this time with the well know main queue. This arranges for that code to run in the main thread, which is mandatory for any UI work.
Also, you really should read the documentation on GCD and especially blocks, because there are memory and cycle considerations. Good luck.
dispatch_queue_t workQ = dispatch_queue_create("bgWorkQ", 0);
dispatch_async(workQ, ^{
// This code is now running in a background thread.
// Do all your loading here...
// When ready to touch the UI, you must do that in the main thread...
disptach_async(dispatch_get_main_queue(), ^{
// Now, this code is running in the main thread.
// Update your UI...
});
});
dispatch_release(workQ);
The easiest way is to use NSObject's - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg You just pass it a selector and it will happy in the background and not block your UI. Be aware however that there are rules to background threads.
You shouldn't update your UI from the background thread. And you need to make sure the methods you're calling are thread safe.