0

I am trying to show a UIActivityIndicatorView at the same location of the UIBarButtonItem it replaces. It shifts to the right while animating. How can I center it without changing the style?

Here is the code:

@IBAction func upload(_ sender: Any) {
        
        let item = sender as! UIBarButtonItem
        let indicatorView = UIActivityIndicatorView(style: .medium)
        indicatorView.translatesAutoresizingMaskIntoConstraints = false
        item.customView = indicatorView
        indicatorView.startAnimating()
        DispatchQueue.global().async {
            Thread.sleep(forTimeInterval: 5) 
            DispatchQueue.main.async {
                item.customView = nil
            }
        }
    }

2 Answers2

1

I would create a custom UIView that contains both the text/icon for the bar button item and the activity indicator, all with constraints set and when I need it do load/animate or not I'd do it directly on that custom view and hide/show the elements accordingly. TheUIBarButtonItem is initialized with that custom view.

cnf
  • 34
  • 3
1

Observations - iPhone SE (iOS 14.6)

A UIBarButtonItem with an icon size of say 24x24 ends up being 43x44 in UINavigationBar.

UIActivityIndicatorView(style: .medium) has a size of 20x20.

You can create a container UIView instance that wraps UIActivityIndicatorView to provide necessary horizontal padding and supply this container to UIBarButtonItem.customView.

Code

    @IBAction func upload(_ sender: Any) {
        let item = sender as! UIBarButtonItem

        let containerView = UIView(frame: .zero)
        containerView.translatesAutoresizingMaskIntoConstraints = false
        
        let indicatorView = UIActivityIndicatorView(style: .medium)
        indicatorView.translatesAutoresizingMaskIntoConstraints = false
        containerView.addSubview(indicatorView)
        
        NSLayoutConstraint.activate([
            containerView.widthAnchor.constraint(equalToConstant: 43),
            containerView.heightAnchor.constraint(equalToConstant: 44),
            indicatorView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8),
            indicatorView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor),
        ])
        
        item.customView = containerView
        indicatorView.startAnimating()
        
        DispatchQueue.global().async {
            Thread.sleep(forTimeInterval: 5)
            DispatchQueue.main.async {
                item.customView = nil
            }
        }
    }

Please note that these size values 43x44 and right padding 8 may change depending on following factors -

  1. iPhone vs iPad
  2. Portrait vs Landscape
  3. Different versions of iOS

You should check this on all iOS versions that you want to support and test again with each new iOS version published going forward.

Tarun Tyagi
  • 9,364
  • 2
  • 17
  • 30
  • Thanks a lot for the detailed answer. I’ve tried the code and checked all the factors, but the issue remained the same. – Boting Wang Jun 07 '21 at 15:37