Eli Stone
Eli Stone

Reputation: 1555

Animate Spinner when loading new page

I have a UITableView which from an external RSS feed.

When you select a row it uses navigationController and slides in from the right, the problem is that the RSS feed contains images therefore it can can take a few seconds to load and without any indication of what is going on you can mistake it for an application crash.

I decided to add a spinner so that you know that new page is loading.

Here is my code:

RootViewController.m

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"Loading New Page");

[tableView deselectRowAtIndexPath:indexPath animated:YES];
DetailsViewController *detailViewController = [[DetailsViewController alloc] initWithNibName:@"DetailsViewController" bundle:nil];
detailViewController.item = [rssItems objectAtIndex:floor(indexPath.row/2)];
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];

UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.center = CGPointMake(160, 240);
[self.view addSubview:spinner];
[spinner startAnimating];
[spinner release];

}

DetailsViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];

        NSString *imgURL = [item objectForKey:@"image"];
        NSData *mydata = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imgURL]];
        item_photo.image = [[UIImage alloc] initWithData:mydata];

    item_title.text = [item objectForKey:@"title"];
    item_date.text = [NSString stringWithFormat:@"Date: %@",[item objectForKey:@"date"]];
    item_time.text = [NSString stringWithFormat:@"Time: %@",[item objectForKey:@"time"]];
    item_cost.text = [NSString stringWithFormat:@"Cost: £%@",[item objectForKey:@"cost"]];
    item_info.text = [item objectForKey:@"description"];
    self.navigationItem.title = @"Event Type";
}

There are two problems with this code.

  1. The Spinner does not active until after the new page has loaded.
  2. The Spinner does not disable once loaded.

If anyone could help me with this problem i would be truly gratefully.

Upvotes: 11

Views: 34544

Answers (3)

This is a spinning wheel over a blurred view in swift:

func blurScence(){
    let blurEffect: UIBlurEffect = UIBlurEffect(style: .Dark)
    let blurView: UIVisualEffectView = UIVisualEffectView(effect: blurEffect)
    blurView.translatesAutoresizingMaskIntoConstraints = false
    blurView.frame = self.view.frame

    let spinner = UIActivityIndicatorView(activityIndicatorStyle:.White)
    spinner.center=blurView.center
    blurView.addSubview(spinner)
    spinner.startAnimating()

    self.view.addSubview(blurView)
}

Upvotes: 1

ikuramedia
ikuramedia

Reputation: 6058

You need to do some work in a background thread. If the following line is the one that takes the time:

detailViewController.item = [rssItems objectAtIndex:floor(indexPath.row/2)];

Then you could do this in the background with GCD:

UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // This is the operation that blocks the main thread, so we execute it in a background thread
    id item = [rssItems objectAtIndex:floor(indexPath.row/2)];

        // UIKit calls need to be made on the main thread, so re-dispatch there
        dispatch_async(dispatch_get_main_queue(), ^{
            detailViewController.item = item;
            [spinner stopAnimating];
        });
});

And +1 to @wattson12 - you need to add the spinner to the new view instead. Alternatively you could add the spinner to the current view, and instead put the pushViewControllercall into your GCD main-queue block.

Final point - you'll want to remove the spinner from its superview once you stop it animating. Alternatively, you can have a single instance of the spinner, and set hidesWhenStopped to YES.

Upvotes: 3

wattson12
wattson12

Reputation: 11174

You are adding the activity indicator view to the view of the controller which is pushing the detail view controller, so you wont see it anyway

try moving the second group of code to the viewDidLoad method of DetailsViewController, you can call stopAnimating on the activity indicator when you are finished loading. To get a reference to the UIActivityIndicator you should add a tag

e.g. in viewDidLoad

UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.center = CGPointMake(160, 240);
spinner.tag = 12;
[self.view addSubview:spinner];
[spinner startAnimating];
[spinner release];

in the loadingFinished method (whichever method is called when finished loading)

[[self.view viewWithTag:12] stopAnimating];

Upvotes: 26

Related Questions