IOS,when an UIView get fully loaded - ios

I have an PlayPageViewController as rootViewController. The PlayPageViewController will display 3D models and UIImages in an method call editPages(), which will takes several seconds to wait.
I just want to add an loadingView at start and when PlayPageViewController gets fully loaded it will disappear.
Here is my solution:
Add an loadingView with activityIndicator.
When the loadingView is loaded, I will begin to implement
but seems it didn't work
STLoadingViewController *loadingView =
[[STLoadingViewController alloc]initWithNibName:#"STLoadingViewController"
bundle:nil];
loadingView.view.frame=CGRectMake(0, 0, 1024, 768);
[self.view insertSubview:loadingView.view atIndex:3];
if(loadingView.isViewLoaded && loadingView.view.window)
{
[self.view insertSubview:self.playPageViewController.view atIndex:4];
[self.playPageViewController setEditingPage:pageIndex];
[loadingView.view removeFromSuperview];
}

You have to do your respective methods to call in viewDidAppear method when this method is called all the appearing task had been finished.

What is the ViewLoad() method? Do you mean viewDidLoad:? You could setup all your views in the storyboard, including a view containing the activity indicator. Don't load the model at this point but wait until viewDidLoad: is called. At this point you may use Grand Central Dispatch to start the loading of the model. It could look a bit like this:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// You need to setup the activity indicator outlet in the storyboard
[_activityIndicator startAnimating];
}
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Do the expensive work on the background
[NSThread sleepForTimeInterval:5.0f];
// All UI related operations must be performed on the main thread!
dispatch_async(dispatch_get_main_queue(), ^{\
// Replace the view containing the activity indicator with the view of your model.
[_activityIndicator stopAnimating];
NSLog(#"STOP");
});
});
}
Edit: The link seems to be down. This is probably due to the current problems with the Dev Center. You can find the documentation in the documentation pane of the Xcode Organizer.

Related

What is a good concept to implement activity indicator

In our iPgone & iPad app we use push segue transitions between different ui contollers, most of them extend UICollectionViewController. In each controller we load data from our internal API. Loading is done viewWillAppear or viewDidLoad.
Now, the thing is, that this API call sometime can take a second or two, or even three... well, lot's of stuff there, let's assume we can't change it. But, we can change the user experience and at least add the "loading" circle indicator. The thing is, what I can't understand by means of correct concept, while transition from A to B, the "load" is done at B, while page A still presented.
So, question is "how do I show indicator on page A, while loading controller for page B?"
Thanks all,
Uri.
Common approach in this case is to load data in destination view controller NOT in main thread. You can show indicator while loading data in background thread and then remove it.
Here is sample of code from my project solving the same problem:
- (void) viewDidLoad {
...
// add indicator
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.spinner.hidesWhenStopped = YES;
self.spinner.center = self.view.center;
[self.view addSubview:self.spinner];
...
// fetch news
[self.spinner startAnimating];
__weak typeof(self) weakSelf = self
[[BitrixApiClient sharedInstance] getLatestNewsWithCompletionBlock:^(NSArray *newsArray, NSUInteger maxPageCount, NSUInteger currentPageNumber, NSError *error) {
if (!error) {
weakSelf.newsArray = newsArray;
weakSelf.currentPageNumber = currentPageNumber;
[weakSelf.newsTableView reloadData];
}
// stop spinning
[weakSelf.spinner stopAnimating];
}];
}

UIActivityIndicatorView not rendering

I have an app that makes web service calls to obtain data. I want to add an activity indicator that is visible when the app is fetching web service data. I have looked into other posts, and though I believe I am doing as the posts recommend, my indicator does not render on the screen. The object that makes the web service call is stateGauges. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
UIActivityIndicatorView *activityStatus = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(120, 230, 50, 50)];
activityStatus.center = self.view.center;
[self.view addSubview:activityStatus];
[activityStatus bringSubviewToFront:self.view];
[UIApplication sharedApplication].networkActivityIndicatorVisible = TRUE;
[activityStatus startAnimating];
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
[activityStatus stopAnimating];
}
Any suggestions? Thanks! V
Your problem is that your animation start is blocked by whatever you're doing in your GuagesList initializer.
When you tell the activity indicator to start animating, it doesn't immediately render to the screen but rather flags the view as needing an update on the next turn of the run loop. Your initializer then blocks the thread until its done, you call stopAnimating, and then the thread has a chance to update the indicator. By which point its already set to not animate.
The best solution is to perform your initializer on another thread using GCD. And be sure to switch back to the foreground thread before calling stopAnimating.
The usual pattern is do something like:
[activityStatus startAnimating];
// enqueue it
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
// now switch back to main thread
dispatch_async(dispatch_get_main_queue(), ^{
[activityStatus stopAnimating];
});
});
You'll want to verify the code as I had to type this from memory on a Windows machine.
take out
[activityStatus bringSubviewToFront:self.view];
because according to the docs bringSubviewToFront:
Moves the specified subview so that it appears on top of its siblings.
which isn't what you want. (another answer suggested you do [self.view bringSubviewToFront:activityStatus] instead.. that's fine, but generally this call is redundant, b/c
[self.view addSubview:activityStatus] adds the activityStatus to the end of the views in the self.view subviews array anyways)
if that still don't work.. basically put a break point right after you start animating, then type this into the console:
[[activityStatus superview] recursiveDescription]
recursiveDescription will give you a UI tree graph and basically tell you exactly where the activityIndicator view is.. you may have made an incorrect assumption about something.
Change
[activityStatus bringSubviewToFront:self.view];
To
[self.view bringSubviewToFront:activityStatus];

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.

Performing data intensive calculations during view init time

Folks,
I'd like to get your opinions on the following scenario. Most screens on my app are table views where the number of rows and contents of the table view is determined by first reading data from the local core data tables and then performing some complex calculations on it. I'd like to do this in a way where the app does not freeze while the user is transitioning from one screen to another. Here is how I have done it. In the view did appear function I start animating an activity indicator and then spawn a thread to read data from the core data tables and perform all the relevant calculations on it. Inside this thread, upon completion of the calculations, I stop animating the activity indicator, mark a flag that initialization is complete and then reload the table view. Load of table view cells before the initialization is complete will return empty cells. (I noticed that the table view data source functions are called immediately after viewWillAppear and before ViewdidAppear()). Pasted below is my code:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"%s",__FUNCTION__);
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"%s",__FUNCTION__);
[activityOutlet startAnimating];
dispatch_async(myQueue, ^{ [self getFromCoreData];
});
}
- (void) getFromCoreData {
// Get from coredata and start calculations here
[activityOutlet stopAnimating];
activityOutlet.hidden = YES;
[tableOutlet reloadData];
}
I'd like to know if there is a better way of doing the above.
Thanks in advance for your responses!
UI updates must be done on the main thread:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self getFromCoreData];
dispatch_async(dispatch_get_main_queue(), ^{
activityOutlet stopAnimating];
activityOutlet.hidden = YES;
[tableOutlet reloadData];
});
});
}
- (void) getFromCoreData {
// Get from coredata and start calculations here
}

UIActivityIndicatorView not showing until after loading done

I have a button on the currently navigated to viewcontroller, connected to an IBAction.
In the IBAction I create a UIActivityIndicatorView as usual, with [self.view addSubView], then load some pictures.
I've tried setNeedsDisplay on the indicator view, the view controller, and the window, but it still loads the pictures before showing the indicator, which of course is quite useless to me.
So I'm looking for a way to either force an instant redraw (which when I think a little more about it is unlikely to make work), or a way to load the pictures after the indicator has appeared, or a way to launch a separate thread or similar to start animating / show the indicator, or put the indicator in a separate viewcontroller and somehow force it to add/show itself before going on to the picture-loading.
Recommendations?
What I do in this situation is spawn a new thread, which frees up the main thread to handle UI interaction while stuff is loading in the background.
First show the UIActivityIndicatorView, then spawn a new thread that loads the images, then on the last line of the method that is executed in the new thread, hide the UIActivityIndicatorView.
Here's an example:
//do stuff...
[activityIndicatorView startAnimating];
[NSThread detachNewThreadSelector:#selector(loadImages) toTarget:self withObject:nil];
In your loadImages method:
- (void) loadImages {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//load images...
[activityIndicatorView performSelectorOnMainThread:#selector(stopAnimating)];
[pool drain];
}

Resources