Reputation: 1874
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
Reputation: 4888
Updated for Swift 5.2, Xcode 11.4
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
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
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
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
Reputation: 10069
Just create two different UIBarButtonItem
s
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