1
votes

In my UIViewController's loadView method I start a UIActivityIndicator. Later in loadView I load a complex UIView, which takes about 2-3 secs to load. (UIScrollView with a lot of pictures more specifically). My problem is that the UIActivityIndicator spinner is only visible, after my other layer has loaded too. (which is of course useless to me). What is a correct way to handle this?

- (void)loadView {

    CGRect fullScreenRect=[[UIScreen mainScreen] bounds];

    UIView *contentView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 340, fullScreenRect.size.width)]autorelease];
    self.view = contentView;

    spinner = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
    spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
    spinner.center = self.view.center;
    [self.view addSubview:spinner];
    [spinner startAnimating];


     scrollView=[[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 340, fullScreenRect.size.width)]autorelease];
     ...setting up scrollView...

     [self.view addSubview:scrollView];
}

- (void)viewDidLoad
{ 
    [super viewDidLoad];
    [spinner removeFromSuperview];
}

I have found a somewhat similar thread: UIActivityIndicatorView not showing until after loading done

But it suggests to load things in a background thread, but loading and displaying a UIView is only possible in the main thread as far as I know.

I am a beginner, and sorry if my question is fundamentally wrong.

3
A couple of options I see - you could render the view to an image off-screen in a background thread and when done, add the image of the view to your scroll view, or you could implement scroll view tiling and load your scroll view content on demand. There are probably other optimizations to be had as well - run Instruments. If you need more help understanding these options let me know. - Carl Veazey
Why are you using loadView and not using a nib? You are a beginner but are trying to play with advance techniques and its going to make what you need to do really hard. So use a nib with a view. When you get viewDidLoad, then add the spinner to your view. Provide a method to turn the spinner off and update your view. In viewDidLoad use a dispatch_async() on the default queue to do the background heavy work. When that is done, have it send another message via dispatch_async(dispatch_get_main_queue() telling your viewController the work is done. This will take you several days so plan on it. - David H

3 Answers

2
votes

I managed to make it the way Carl Veazey suggested. It was pretty easy actually. I spawned a new background thread, and loaded my UIScrollView there.

I used, in LoadView:

spinner = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
    spinner.center = self.view.center;
[self.view addSubview:spinner];
[spinner startAnimating];

[self performSelectorInBackground:@selector(populateScrollView) withObject:nil];

And:

-(void) populateScrollView{

    ...creating scrollView...

    [self.view addSubview:scrollView];
    [spinner removeFromSuperview];

}

It works perfectly. What is really weird for me, is that I am adding the scrollview to the UI in a background thread, not in the main thread. I thought I could only mess with the UI in the main thread.

(I could do the same thing with GCD. When the background thread finished loading the scrollview, I could display scrollview in the main thread's queue. But the former solution somehow works as well...)

2
votes

Sorry for ugly text formatting. There is a very cute way to do what you want. Firstly you have to show the indicator view and only after a little amount of time (0.1 sec or even lesser) you ask your scrollView to populate.

- (void)loadView {

    CGRect fullScreenRect=[[UIScreen mainScreen] bounds];

    UIView *contentView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 340, fullScreenRect.size.width)]autorelease];
    self.view = contentView;

    spinner = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
    spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
    spinner.center = self.view.center;
    [self.view addSubview:spinner];
    [spinner startAnimating];

    double delayInSeconds = 0.1;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        scrollView=[[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 340, fullScreenRect.size.width)]autorelease];
     ...setting up scrollView...

       [self.view addSubview:scrollView];
    });
}
0
votes

Try this simple method, Its working well for me....

UIActivityIndicatorView *activityIndicator= [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(0, 0, 50, 50)];
activityIndicator.layer.cornerRadius = 05;
activityIndicator.opaque = NO;
activityIndicator.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.6f];
activityIndicator.center = self.view.center;
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[activityIndicator setColor:[UIColor colorWithRed:0.6 green:0.8 blue:1.0 alpha:1.0]];
[self.view addSubview: activityIndicator];

Add following lines while load your view

//-- Add this line while processing to load your view
    [activityIndicator startAnimating];

//-- Add this line when after you view loaded
    [activityIndicator stopAnimating];