0
votes

I've got a nav bar button item that is adding a custom activity indicator view during a data query. When the query completes, I'd like to set the button back to it's original state. Here is my current code:

let filterActivityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
filterActivityIndicator.color = .yandasRed
filterMenuNavButton.customView = filterActivityIndicator
filterActivityIndicator.startAnimating()
ProductsFilterData.searchShared.updateFilteredCollectionData(source: "Search") { (done) in
        filterActivityIndicator.stopAnimating()
        self.filterMenuNavButton.setBackgroundImage(#imageLiteral(resourceName: "icons8-slider-30"), for: .normal, barMetrics: .default)
}

I've tried setting the custom background to nil, which does nothing. I've tried just re-setting the bar button item's background image, which does nothing. Any ideas? Thanks!

EDIT: The UIBarButtonItem I'm trying to manipulate is on the right side, but there are two bar button items on the right. filterMenuNavButton is an IBOutlet for the specific button I'm trying to manipulate.

2

2 Answers

1
votes

Sometimes invalidation is a problem. It seems you will need to set the button to the navigation bar again. It is so smart that you first need to set it to nil for the effect to even take place.

This is what I tried it with:

let buttonImage: UIImage = #imageLiteral(resourceName: "icons8-slider-30")

let filterMenuNavButton = UIBarButtonItem(image: buttonImage, style: .plain, target: nil, action: nil)
self.navigationItem.rightBarButtonItem = filterMenuNavButton

DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    let filterActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
    filterMenuNavButton.customView = filterActivityIndicator
    filterActivityIndicator.startAnimating()
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
        filterActivityIndicator.stopAnimating()
        filterMenuNavButton.customView = nil
        filterMenuNavButton.setBackgroundImage(buttonImage, for: .normal, barMetrics: .default)
        self.navigationItem.rightBarButtonItem = nil
        self.navigationItem.rightBarButtonItem = filterMenuNavButton
    }
}

I am not sure what your setup is but I took a simple navigation view controller and put this code in viewDidLoad of it's current (root) view controller.

The last part is probably what you are looking for and you need to:

  • Set custom view to clear
  • Add image or change other button properties
  • Clear button item (set to nil)
  • Set the button item back to desired one

I hope this solves your issue. I must say though that it would probably make more sense to create a new button item when indicator is in progress. Check the following:

DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    let preservedButtonItem = self.navigationItem.rightBarButtonItem

    let filterActivityIndicator: UIActivityIndicatorView = {
        let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: activityIndicator)
        return activityIndicator
    }()

    filterActivityIndicator.startAnimating()
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
        filterActivityIndicator.stopAnimating()
        self.navigationItem.rightBarButtonItem = preservedButtonItem
    }
}

EDIT: Multiple buttons

For multiple buttons the situation should be pretty similar. Please try playing with the following:

guard let oldItems = navigationItem.rightBarButtonItems else {
    return
}

var newItems = oldItems

// Find the index of item so we may ereplace it
guard let index = newItems.index(where: { $0 === filterMenuNavButton }) else {
    return
}
let activityIndicator: UIActivityIndicatorView = {
    let activity = UIActivityIndicatorView()
    return activity
}()
newItems[index] = UIBarButtonItem(customView: activityIndicator)

navigationItem.rightBarButtonItems = newItems
DispatchQueue.main.asyncAfter(deadline: .now()+2.0) {
    self.navigationItem.rightBarButtonItems = oldItems
}
0
votes

I've had no problem doing this using as custom view as the bar button item:

fileprivate var underlyingButton: UIButton?

...

let barButton = UIButton(type: .custom)
barButton.addTarget(self, action: #selector(stuffs), for: .touchUpInside)
barButton.setImage(image, for: UIControlState())

let rightItem = UIBarButtonItem(customView: barButton)
self.filterMenuNavButton = rightItem

self.underlyingButton = barButton

Then later I can call simply do:

guard let button = underlyingButton else {
    return
}
button.setImage(someOtherImage, for: UIControlState())