Luca
Luca

Reputation: 1874

Replace UIBarButtonItem with UIActivityIndicatorView

I want to replace my UIBarButtonItem (used for refresh) with a UIActivityIndicatorView and, when the refresh is finished, I want to turn back to the refresh button and remove the UIActivityIndicatorView.

Upvotes: 41

Views: 12792

Answers (5)

rbaldwin
rbaldwin

Reputation: 4888

Updated for Swift 5.2, Xcode 11.4

enter image description here

class ViewController: UIViewController {

    var activityIndicator = UIActivityIndicatorView()
    var refreshBarButton = UIBarButtonItem()
    var activityBarButton = UIBarButtonItem()

    override func viewDidLoad() {
        super.viewDidLoad()

        activityIndicator.sizeToFit()
        activityIndicator.color = self.view.tintColor

        activityBarButton = UIBarButtonItem(customView: activityIndicator)
        refreshBarButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshBarButtonPressed))

        showRefreshButton()
    }

    func performNetworkOperation(completion: @escaping()->()) {
        //simulate network operation
        DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
        completion()
        })
    }

    @objc func refreshBarButtonPressed() {

        showActivityIndicator()
        activityIndicator.startAnimating()

        performNetworkOperation {
            self.activityIndicator.stopAnimating()
            self.showRefreshButton()
        }
    }

    func showRefreshButton() {
        self.navigationItem.setRightBarButton(refreshBarButton, animated: true)
    }

    func showActivityIndicator() {
        self.navigationItem.setRightBarButton(activityBarButton, animated: true)
    }
}

Upvotes: 16

nikw
nikw

Reputation: 71

I was trying to do the same thing, and I thought setting self.navigationItem.rightBarButtonItem didn't work because the activity indicator wouldn't display. Turns out it was working fine, I just couldn't see it because I have a white navigation bar and the default UIActivityIndicatorView style is also white. So it was there but invisible. With the gray style I can now see it.

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

(Duh.)

Upvotes: 7

Ian
Ian

Reputation: 7558

I used a similar technique to update a button in a UIToolbar when a UIWebView is reloading (as it doesn't appear to be possible to show/hide individual bar button items). In this case, you need to swap out all of the items in the UIToolbar.

@property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshBarButton;

@property (nonatomic, strong) UIActivityIndicatorView *activityView;
@property (nonatomic, strong) UIBarButtonItem *activityBarButton;

@property (strong, nonatomic) IBOutlet UIToolbar *toolbar;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *backBarButton;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshBarButton;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *forwardBarButton;

#pragma mark - UIWebViewDelegate
-(void)webViewDidFinishLoad:(UIWebView *)webView{
    [self updateButtons];
}

-(void)webViewDidStartLoad:(UIWebView *)webView{
    [self updateButtons];
}

-(void)updateButtons{
    /*
     It's not possible to show/hide bar button items so we need to do swap out the toolbar items in order to show the progress view
     */

    //Initialise the activity view
    if (self.activityBarButton == nil){
        self.activityView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
        self.activityBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.activityView];
        self.activityBarButton.enabled = NO;
    }


    NSMutableArray *toolbarItems = [[NSMutableArray alloc] initWithArray:self.toolbar.items];

    if ([self.webview isLoading]){
        //Replace refresh button with loading spinner
        [toolbarItems replaceObjectAtIndex:[toolbarItems indexOfObject:self.refreshBarButton]
                                withObject:self.activityBarButton];

        //Animate the loading spinner
        [self.activityView startAnimating];
    }
    else{
        //Replace loading spinner with refresh button
        [toolbarItems replaceObjectAtIndex:[toolbarItems indexOfObject:self.activityBarButton]
                                withObject:self.refreshBarButton];

        [self.activityView stopAnimating];
    }

    //Set the toolbar items
    [self.toolbar setItems:toolbarItems];


    //Update other buttons
    self.backBarButton.enabled = [self.webview canGoBack];
    self.forwardBarButton.enabled = [self.webview canGoForward];
}

Upvotes: 2

Nassif Bourguig
Nassif Bourguig

Reputation: 765

Here what works for me :

- (void) rightItemButtonWithActivityIndicator
{
    UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
    [activityIndicator startAnimating];
    UIBarButtonItem *activityItem = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
    [activityIndicator release];
    self.navigationItem.rightBarButtonItem = activityItem;
    [activityItem release];
}

Upvotes: 11

Tom Irving
Tom Irving

Reputation: 10069

Just create two different UIBarButtonItems

One for the activity indicator and another for a normal UIBarButtonItem.

UIActivityIndicatorView * activityView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
[activityView sizeToFit];
[activityView setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin)];
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:activityView];
[self.navigationItem setRightBarButtonItem:loadingView];
[loadingView release];
[activityView release];

UIBarButtonItem * normalButton = [[UIBarButtonItem alloc] initWithTitle...];
[self.navigationItem setRightBarButtonItem:normalButton];
[normalButton release];

When you want to switch them, just reassign the rightBarButtonItem to whichever.

Upvotes: 53

Related Questions