Blocking the main thread immediately in the first view cause a flash of black screen on iOS 8 - ios

Adding the following code to a new project cause a flash of a black screen. It seems to be caused by the fade out animation of the splash screen. Unfortunately, the long process must be on main thread. It is possible to avoid it by delaying it but it is unreliable and lengthens the loading process.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// A spinner is shown
// This most probably will not cause a black screen
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// [self loadSomething];
// });
// This will not cause a black screen but not suitable for my use
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// [self loadSomething];
// });
dispatch_async(dispatch_get_main_queue(), ^{
[self loadSomething];
});
}
- (void)loadSomething
{
NSLog(#"start long process");
NSDate *start = [NSDate date];
while (-[start timeIntervalSinceNow] < 5) {
}
}

Consider why it must be on the main thread and come up with a way to move this processing or minimise it.
You should create a root view controller which displays the app default image and have that view controller displayed (probably with some animation or progress indication) until your processing has completed.

If you're already on your main thread, then there is no need of getting the main thread asynchronously.
dispatch_async(dispatch_get_main_queue(), ^{
});
The above code should be removed.
In case you want to do something or load something, then you should use
performSelectorInBackground
Hope this helps...

Related

better way of using GCD for threading

Ok. So i am trying to use GCD To Handle all the heavy loading before transition to next view controller. I am opening large archive Files and extracting them which takes some time.
The entire Process is like this:
Click a UICollectionViewCell>Display activity indicator>Let GCD take care of heavy loading>call transition selector using performSelector: onThread:.....
The problem is when i use mainThread, the transition occurs too fast and all the heaving loading don't come to effect until after some time and the transition looks awful and while using currentThread, well it just takes so much time, it seems plain awful of a app.
-(void)someMethod
{
//activity Indicator before transition begins
UIActivityIndicatorView *activity=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activity setFrame:self.view.bounds];
[self.view addSubview:activity];
[self.view bringSubviewToFront:activity];
activity.hidesWhenStopped=YES;
[activity startAnimating];
dispatch_queue_t transitionQueue;
transitionQueue = dispatch_queue_create("com.app.transitionQueue", NULL);
dispatch_async(transitionQueue,^{
//heavy lifting code
viewerPVC=.....
dispatch_async(dispatch_get_main_queue(),^{
[activity stopAnimating];
[self transitionToMangaViewer:mReaderPVC];
});
};
}
-(void)transitionToViewer:(ViewerPVC*)viewerPVC
{
[self.navigationController pushViewController:mReaderPVC animated:YES];
}
So Tried The First Suggestion, but the transition still seems buggy since the CollectionViewController still remain On Background For Some Time after the transition
you shouldnt need to use NSThread when you are using gcd, try something like this instead
dispatch_async(transitionQueue,^{
//heavy lifting code
viewerPVC=..... //this should block here otherwise will not work
dispatch_async(dispatch_get_main_queue(), ^{
[activity stopAnimating];
[self transitionToAnotherViewer:viewerPVC];
});
});
UI Update should be done on Main Thread, no need to create New Thread for performing UI Transition. Try below code:
dispatch_async(transitionQueue,^{
//heavy lifting code
viewerPVC=.....
dispatch_async(dispatch_get_main_queue(), ^{
[activity stopAnimating];
[self performSelector:#selector(transitionToAnotherViewer:) withObject:viewerPVC waitUntilDone:YES];
});
};

Effective way to use NSThread and autorealease pools in iOS

I'm using the MBProgressHUD library in my app, but there are times that the progress hud doesn't even show when i query extensive amount of data, or show right after the processing of data is finished (by that time i don't need the hud to be displayed anymore).
In another post i found out that sometimes UI run cycles are so busy that they don't get to refresh completely, so i used a solution that partially solved my problem: Now every request rises the HUD but pretty much half the times the app crashes. Why? That's where I need some help.
I have a table view, in the delegate method didSelectRowAtIndexPath i have this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[NSThread detachNewThreadSelector:#selector(showHUD) toTarget:self withObject:nil];
...
}
Then, I have this method:
- (void)showHUD {
#autoreleasepool {
[HUD show:YES];
}
}
At some other point I just call:
[HUD hide:YES];
And well, when it works it works, hud shows, stays and then disappear as expected, and sometimes it just crashes the application. The error: EXC_BAD_ACCESS . Why?
By the way, the HUD object is already allocated in the viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
...
// Allocating HUD
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.labelText = #"Checking";
HUD.detailsLabelText = #"Products";
HUD.dimBackground = YES;
}
You need to perform your processing on another thread, otherwise the processing is blocking MBProgressHud drawing until it completes, at which point MBProgressHud is hidden again.
NSThread is a bit too low-level for just offloading processing. I'd suggest either Grand Central Dispatch or NSOperationQueue.
http://jeffreysambells.com/2013/03/01/asynchronous-operations-in-ios-with-grand-central-dispatch
http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues
/* Prepare the UI before the processing starts (i.e. show MBProgressHud) */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* Processing here */
dispatch_async(dispatch_get_main_queue(), ^{
/* Update the UI here (i.e. hide MBProgressHud, etc..) */
});
});
This snippet will let you do any UI work on the main thread, before dispatching the processing to another thread. It then returns to the main thread once the processing is done, to allow you to update the UI.

How to update UIView before next method is called in Objective-C

I'm trying to update my textView on screen before it starts downloading data. Right now, it only updates the view after all of the downloads are complete. How can I do it before or in between the downloads?
Edit: I want the self.textView.text = #"Connection is good, start syncing..."; to update the UI before the downloading starts. But right now, it only updates after the download finishes.
Here is what the code looks like.
if ([self.webApp oAuthTokenIsValid:&error responseError:&responseError]) {
self.textView.text = #"Connection is good, start syncing...";
[self.textView setNeedsDisplay];
[self performSelectorInBackground:#selector(downloadCustomers:) withObject:error];
}
I'm new to this and have yet to learn how threads work, but from what I read, the downloadCustomers function should be using a background thread leaving the main thread to update the UI.
if ([self.webApp oAuthTokenIsValid:&error responseError:&responseError]) {
self.textView.text = #"Connection is good, start syncing...";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self downloadCustomers];
dispatch_async(dispatch_get_main_queue(), ^{
//Do whatever you want when your download is finished, maybe self.textView.text = #"syncing finished"
});
});
}
The pattern here is to initialize your download on background thread and then call back to main thread for UI update.
Below is an example using GCD. The advantage of GCD version is that you can consider using whatever you do in -downloadCustomers, to insert in-line where you call it.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self downloadCustomers];
dispatch_async(dispatch_get_main_queue(), ^{
[self.textView setNeedsDisplay];
});
});

Main Thread processing issue

In my application i am using back ground thread for hitting multiple service and perform operation with core data. I have used main thread for back ground process ,Its working fine.
Here is my code
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(main,
^{
[self backGroundCall];
});
-(void)backGroundCall
{
NSLog(#"Done");
if([CacheManager refreshDBforFirstTimeUseWithDelegate:self])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"IsDBInitialized"];
ContainerViewController *containerViewControllerInstance = [ContainerViewController getContainerInstance];
[containerViewControllerInstance setUserId:_userID];
[progressView setHidden:YES];
[self.view setUserInteractionEnabled:YES];
[self.navigationController setDelegate:containerViewControllerInstance];
[self.navigationController pushViewController:containerViewControllerInstance animated:YES];
}
}
once i initialize the data base , i need to navigate to the container view.During the initialization i will display one progress bar. That is working fine, when the entire background process is completed(app is in minimized state). During the background process if i come to the foreground progress bar is not showing at that time black screen is display instead of progress view . After the completion of the main threat container view all not display[if i comes to foreground of main thread process].
i need to show the progress bar, if i come back to the app in the middle of the main thread process. Please guide me to fix this issue.
Thanks.
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(main,
^{
[self backGroundCall];
});
This is a bit misleading... You call the method backGroundCall, but you are actually doing this on the main thread. If you want to make some operation on a working thread, you can do this:
// Declare the queue
dispatch_queue_t workingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(workingQueue,
^{
// My background job
dispatch_async(dispatch_get_main_queue(),
^{
// Update the UI
}
);
});

How can I display a waiting progress bar before the application started on an IPhone

It takes a long to start up the application I write on an iPhone. I hope to display a waiting progress bar to indicate the percentage of the startup progress. Can anyone help me? I am new to the Development of IOS.
You can't.
The application needs to be started before you can show any dynamic content.
After your first ViewController is loaded you can of course show a progress bar and do loading.
Before that, you can only show a static image Default.png & Default#2x.png.
If you delay the heavy loading to after your -[ViewController viewDidLoad]-method is finished you can show any kind of GUI while doing the heavy work.
Edit:
This example will detach a new thread in your viewDidLoad and do some heavy work (in this case sleep for 1 second) and update a UIProgressView and log the progress as it sleeps 10 times, incrementing the progressview with 0.1 each time.
- (void)viewDidLoad
{
// .....
[NSThread detachNewThreadSelector:#selector(workerBee) toTarget:self withObject:nil];
}
- (void)workerBee
{
NSAutoreleasePool* workerPool = [[NSAutoreleasePool alloc] init];
// Put your image loading between this line and [workerpool release]
for (float i = 0; i<1; i+= 0.1)
{
[self performSelectorInBackground:#selector(updateProgress:) withObject:[NSNumber numberWithFloat:i]];
sleep(1000);
}
[workerPool release];
}
- (void)updateProgress:(NSNumber*)number
{
NSLog("Progress is now: %#", number);
[progressView setProgress:[number floatValue]];
}
Place a loading bar in the first screen and start your computation-heavy code in another thread after displaying it.

Resources