Make UIView Appear Before Network Operation - ios

I have a seemingly simple problem that I cannot for the life of me seem to figure out. In my iOS App, I have a UICollectionView that triggers network operation upon tapping it that can take a few seconds to complete. While the information is being downloaded, I want to display a UIView that fills the cell with a UIActivityIndicatorView that sits in the square until the loading is done, and the segue triggered. The problem is that it never appears. Right now my code looks like:
myLoadView.hidden = NO;
//Network Operation
myLoadView.hidden = YES;
The App simply stops for a couple seconds, and then moves on the the next view. I'd imagine Grand Central Dispatch has somthing to do with the solution, however please keep in mind that this code takes place in prepareForSegue, and the network info needs to be passed to the next View. For this reason not finishing the download before switching scenes has an obvious problem. Any help would be VASTLY appreciated. Thanks!

iOS commits changes in the interfaces after working out a routine. Hence you should perform your network operation in a background thread and then get back back on the main and perform the "show my view now thing". Have a look the below code for reference.
myLoadView.hidden = NO;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//Network Operation
dispatch_async(dispatch_get_main_queue(), ^{
myLoadView.hidden = YES;
});
});

Your network operation seems to be carried out on the main thread, aka UI thread. This blocks all further UI calls, including the call to unhide a view, until completion.
To resolve this, make your call asynchronous.
You should read this in full, if you haven't already.

As mentioned by other answers, the problem is that the UIView change doesn't happen until the current method finishes running, which is where you are blocking. Before GCD was available I would split methods in two and use performSelector:withObject:afterDelay (to run the second part also on the UI loop) or performSelectorInBackground:withObject: at the end of the first method. This would commit all the waiting animaations first, then do the actual tasks in the second method.

Well the better option for this type of indication is by using the custom HUD libraries like SVProgressHUD or MBProgressHUD

Related

Swift Xcode6 UIActivityIndicatorView Slow to display

How do I get the UIActivityIndicatorView to display first, then execute other code?
I've experimented with using sleep, and it works but it doesn't "feel" right and adds an extra second to processing a bunch of core data stuff. I've also tried dispatching it to the main thread which only works some of the time. (I'm guessing when the rest of the block is executed outside of the main thread).
Ideally as soon as a user touches the button the instance of the UIActivityIndicatorView would display (which seems to happen where I've used it in other apps by itself or with other minimal processing).
Details: I have an IBAction connected to a button that executes a bunch of core data stuff, sometimes including images, that takes between 1 - 3 seconds to finish. When it finishes it dismisses the view controller. The view controller where this is executed is presented as a modal over current context.
Here is a code snippet:
// get the background queue
let bg_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(bg_queue, {
// long running code here...
dispatch_async(dispatch_get_main_queue(), {
self.activityIndicator.stopAnimating()
})
})

Delay in programmatically displaying a view controller

RESOLVED
I was performing the segue from inside a block and it was another thread. When I specifically move the operation back to the main thread it works great.
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self performSegueWithIdentifier:#"LoadToDisplay" sender:self];
}];
QUESTION
I want to programmatically present a view controller.
My app downloads some data from the internet, carries out some data handling and then loads the next view.
I use performSegueWithIdentifer successfully throughout the rest of the app however for some reason it is adding a huge delay with this specific transition.
I've used presentViewController with no delay but I can't use this, it was just to test whether I was missing something obvious.
I have an NSLog when the last data handling method completes and one when the next view controller is loaded. Using 'presentViewController' the time between logs is 14ms. When using performSegueWithIdentifer is a staggering 8.5secs!
I literally commented out one line and tested with the other. No other code changes.
Has anyone else experienced this or know what might be going on?
Thanks.

MBProgressHUD won't display the label

I'm using MBProgressBar in my app to display feedback whenever there is a call to a certain webService.
To do so, in the method "requestStarted" of ASIHTTPRequest, I call:
[NSThread detachNewThreadSelector:#selector(startLoader) toTarget:self];
Where startLoader is the method that pops the HUD.
Now, the thing is that whenever I call startLoader directly, the HUD gets displayed with no problem, but when I call the method using the detachNewThreadSelector thing (which is needed), the HUD is displayed but with no text label.
If I had to guess, I would say I need to force-refresh the component, but I don't know how to do that.
Anything having to do with the HUD will need to be done on the main/UI thread. If you are detach and put on a background thread, the HUD will likely never get those updates because your request will finish before getting back around to the main thread.

ios UI unresponsive during viewdidappear

I have a table view, and when the user selects a row, i push them to a new ViewController. At first, I initialized all my view objects in the ViewDidLoad method (involving web service calls) but I saw that it made the transition from my tableview to my new viewcontroller very long.
Instead, I moved most of my UI initialization in the ViewDidAppear method, and I like that it sped up my transition from tableview to new viewcontroller.
However, I cannot press any buttons in my NavigationBar at the top of the screen (like the back button) until my ViewDidAppear method completes and the UI is loaded.
What's the solution for this? Is there another way for me to load my UI without it preventing the user from interacting with the buttons in my NavigationBar?
Thanks!!
you do too much on the main thread. off load your longer operations like IO or longer computations BUT take care to not mess with the UI in the background thread.
Only touch the UI on the main thread. (Note sometimes it might seem safe, but in the long run it always end up producing weird issues)
one easy way is to use GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//insert web service requests / computations / IO here
dispatch_async(dispatch_get_main_queue(),^{
//back to the main thread for UI Work
});
});
You could use grand central dispatch to make your web service calls asynchronously, which will keep the UI on the main thread responsive.
//create new queue
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.siteName.projectName.bgqueue", NULL);
//run requests in background on new queue
dispatch_async(backgroundQueue, ^{
//insert web service requests here
});
Here's a more in-depth tutorial:
http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial
Try to initialize your UI in the background by using the following method
[self performSelectorInBackground:#selector(initYourUI) withObject:yourObj];
You can call this in the ViewDidLoad

UIProgressView not updating while my App loads its resources

I am trying to update a UIProgressView progress bar that I have in a UIView during a long resource loading process. Let's say I'm loading a bunch of bitmaps from a NIB file (like maybe a hundred). After I load 10, I issue a .progress to the UIProgressView that is part of a UIView that is already being displayed. So, I issue:
myView.myProgressView.progress=0.2;
Then, I load another 10 bitmaps, and issue:
myView.myProgressView.progress=0.4;
etc., etc. When the app runs, the progress bar doesn't advance. It simply stays at its initial position. At the risk of sounding like a complete moron, do I have to load my resources on a separate thread so the OS can update the UI, or, is there an easier way? Thanks for any assistance.
Yes. Load them on a separate thread. Or just use something like performSelector:
[self performSelector:#selector(setProgressBar) withObject:nil afterDelay:0.0];
(and create a setProgressBar function which reads the current value from a member variable and updates the UI)
You could run a step of the runloop after each update of the UI:
SInt32 result;
do {
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE);
} while(result == kCFRunLoopRunHandledSource);
It could have other bad consequences (for example, enable user to interact with the UI, execute delegates of view controller such as viewDidAppear before they should be executed, etc) so be very, very careful.

Resources