Activity indicator not always rendered - ios

I have code that triggers an activity indicator:
activityIndicator.startAnimating()
Once it is triggered I continue to launch some background tasks (e.g. go to server, fetch data, callback when done).
The problem I have is that the behaviour of the activity indicator is inconsistent in the sense that from displaying on UI it sometimes visible/gets rendered and animating and sometimes does not (especially if the app was in the background and returned to foreground)
I am not sure what I need to do to ensure that it consistently rendered on the screen when I call startAnimating() before I proceed to launch background tasks.
Any guidance would be greatly appreciated.
Thanks.

I think it's because you may work with UI outside main thread. Try wrap code in
dispatch_async(dispatch_get_main_queue(), ^{
// Your code to run on the main queue/thread
self.activityIndicator.startAnimating()
});

Related

What is the best way to ensure the main thread is blocked while a background thread is processing?

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.

Multiple "Main Queue" Tasks For User Perspective

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()
});
});
}

Loading multiple views asynchronous in swift

I was wondering how someone would load multiple UI elements asynchronously. In my case, I have a UIViewController that has a UISegmentControl. Each segment has different UI aspects to load.
For example seg1 shows one image, seg2 shows and image and some text, etc.
If I load all the UI elements before showing the UIViewController then there is a noticeable lag. So I would want to load seg2...n asynchronously to make the UI feel more responsive.
Would I need to load everything via
DispatchQueue.main.async {
// load UI
}
For each seg I want to load in the background? Or can I load these elements on another thread and not take up the main thread? I know you're not supposed to update the UI on background threads... But will using the main thread still block the UI if I use async code?
You can't make UI changes on a background thread. Nearly all UIKit calls must be made on the main thread. Therefore you're likely out of luck.
Instead what you should do is do the time consuming number-crunching (downloading and parsing data for example) on a background thread and then use DispatchQueue.main.async() to install the data into your views on the main thread once the time consuming work is done.
If it's the UI setup that is introducing the lag and it's not possible to speed it up then you may be out of luck.

How to delay the disappearance of splash screen and parallelly run a webservice in background in iOS objective C?

I have integrated branch.io(https://branch.io/) in my app for deep linking.
Here the response that I am getting in the branch handler has a slight delay. Once I get the response I do some other processing and then decide whether to navigate to first screen or the second screen.
The problem is even before getting the branch response, the first screen is displayed. I want to delay the splash screen for a while (which is not good as most of the people say but I do not have any option probably). I referred to various links, How can I display a splash screen for longer on an iPhone? where they suggest to add a splash view. But this is not working. I donot know for what reason. I also used [NSThread sleepForTimeInterval:6.0];
But this blocks the main thread. I also used GCD which didn't help.
In didFinishLaunchingWithOptions, this is what I have coded,
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
dispatch_async(dispatch_get_main_queue(), ^{
//get branch response
});
});
But the first screen is appearing before getting the response from branch.io. Kindly help.... This is major problem in my app without which I cannot test anything.. Please help me... Let me know if I have not made myself clear..
Why not creating screen between splash screen and main screen, that will load needed data and when it's done it will be redirected to main screen. It will say something like wait until data loads and may show some progress indicator. I think it's the best of possible solutions.
You can run webservice in main thread in didFinishLaunchingWithOptions. The splash screen will wailt until the service finished. After that you can choose with screen will be pushed to.
Remember handle the case the API is failed.

UIActivityview indicator help?

I would like the UIActivityindicator to spin as long as a method isn't finshed loading like one of my methods parses data from a website so I would uiactivity indicator to spin as long as the method isn't finished doing its job.
thanks,
Tushar Chutani
the obvious answer is [activityView startAnimating]
however i suspect you are running a synchronous retrieval of the data and you are not getting back control of the UI until you have finished your method.
if that is so you will need to run your method in the background and then do the UI updates on the main thread.
post some code .....

Resources