【问题标题】:Display activity indicator inside UIButton在 UIButton 内显示活动指示器
【发布时间】:2016-08-01 01:57:03
【问题描述】:

我想在按下 UIButton 后将其内容更改为 ActivityIndi​​cator。

我知道按钮有一个 imageView 和一个 titleLabel,但我不知道如何在其中放置一个活动指示器。

这就是我创建活动指标的方式:

let aiView = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
aiView.startAnimating()
aiView.center = CGPointMake(0,0)
aiView.hidesWhenStopped = false

【问题讨论】:

    标签: ios swift uibutton


    【解决方案1】:
    import UIKit
    
    class LoadingButton: UIButton {
    private var originalButtonText: String?
    var activityIndicator: UIActivityIndicatorView!
    
    func showLoading() {
        originalButtonText = self.titleLabel?.text
        self.setTitle("", for: .normal)
        
        if (activityIndicator == nil) {
            activityIndicator = createActivityIndicator()
        }
        
        showSpinning()
    }
    
    func hideLoading() {
        self.setTitle(originalButtonText, for: .normal)
        activityIndicator.stopAnimating()
    }
    
    private func createActivityIndicator() -> UIActivityIndicatorView {
        let activityIndicator = UIActivityIndicatorView()
        activityIndicator.hidesWhenStopped = true
        activityIndicator.color = .lightGray
        return activityIndicator
    }
    
    private func showSpinning() {
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(activityIndicator)
        centerActivityIndicatorInButton()
        activityIndicator.startAnimating()
    }
    
    private func centerActivityIndicatorInButton() {
        let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
        self.addConstraint(xCenterConstraint)
        
        let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
        self.addConstraint(yCenterConstraint)
    }
    }
    

    【讨论】:

    • 基本用途,看起来很棒。 +1
    • 记得将storyboard中的按钮类更新为自定义类。例如。身份检查器 > 自定义类 = LoadingButton。否则你会得到EXC_BAD_ACCESS
    • 如果您使用 IB,请使用 NSAttributedString。因为 IB 设置了 attibutedString。 self.setAttributedTitle(NSAttributedString(string: ""), for: .normal)
    • 我建议添加:activityIndicator.isUserInteractionEnabled = false ....否则触摸activityIndi​​cator不会执行按钮的目标。
    【解决方案2】:

    大多数答案只使用标题,在我的情况下,我还需要管理属性文本和图像,

    import UIKit
    
    class LoadingButton: UIButton {
    
        struct ButtonState {
            var state: UIControl.State
            var title: String?
            var attributedTitle: NSAttributedString?
            var image: UIImage?
        }
    
        private (set) var buttonStates: [ButtonState] = []
    
        private lazy var activityIndicator: UIActivityIndicatorView = {
            let activityIndicator = UIActivityIndicatorView()
            activityIndicator.hidesWhenStopped = true
            activityIndicator.color = self.titleColor(for: .normal)
            self.addSubview(activityIndicator)
            activityIndicator.translatesAutoresizingMaskIntoConstraints = false
            let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
            let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
            self.addConstraints([xCenterConstraint, yCenterConstraint])
            return activityIndicator
        }()
    
        func showLoading() {
            activityIndicator.color = self.titleColor(for: .normal)
            activityIndicator.startAnimating()
            var buttonStates: [ButtonState] = []
            for state in [UIControl.State.disabled] {
                let buttonState = ButtonState(state: state, title: title(for: state), attributedTitle: attributedTitle(for: state), image: image(for: state))
            buttonStates.append(buttonState)
                setTitle("", for: state)
                setAttributedTitle(NSAttributedString(string: ""), for: state)
                setImage(UIImage(), for: state)
            }
            self.buttonStates = buttonStates
            isEnabled = false
       }
    
       func hideLoading() {
           activityIndicator.stopAnimating()
           for buttonState in buttonStates {
               setTitle(buttonState.title, for: buttonState.state)
               setImage(buttonState.image, for: buttonState.state)
               setAttributedTitle(buttonState.attributedTitle, for: buttonState.state)
           }
           isEnabled = true
        }
    }
    

    【讨论】:

      【解决方案3】:

      这是我的编辑,代码少一点。

      class Button: UIButton {
          private var originalButtonText: String?
      
          private lazy var activityIndicator: UIActivityIndicatorView = {
              let activityIndicator = UIActivityIndicatorView()
              activityIndicator.translatesAutoresizingMaskIntoConstraints = false
              activityIndicator.color = .black
              addSubview(activityIndicator)
      
              NSLayoutConstraint.activate([
                  activityIndicator.centerYAnchor.constraint(equalTo: self.centerYAnchor),
                  activityIndicator.centerXAnchor.constraint(equalTo: self.centerXAnchor)
              ])
      
              return activityIndicator
          }()
      
          func loading(_ isLoading: Bool) {
              isEnabled = !isLoading
      
              if isLoading {
                  originalButtonText = titleLabel?.text
                  setTitle("", for: .normal)
                  activityIndicator.startAnimating()
              } else {
                  setTitle(originalButtonText, for: .normal)
                  activityIndicator.stopAnimating()
              }
          }
      }
      

      用法:button.loading(true/false)

      【讨论】:

      • 这个解决方案现在失效了
      • 你能说得具体点吗?
      【解决方案4】:

      Swift 5、Xcode 11.3

      我已针对我的用例对其进行了修改,以包含按钮文本和微调器

      import UIKit
      
      class LoadingButton: UIButton {
      
      var activityIndicator: UIActivityIndicatorView!
      
      let activityIndicatorColor: UIColor = .gray
      
      func loadIndicator(_ shouldShow: Bool) {
          if shouldShow {
              if (activityIndicator == nil) {
                  activityIndicator = createActivityIndicator()
              }
              self.isEnabled = false
              self.alpha = 0.7
              showSpinning()
          } else {
              activityIndicator.stopAnimating()
              self.isEnabled = true
              self.alpha = 1.0
          }
      }
      
      private func createActivityIndicator() -> UIActivityIndicatorView {
          let activityIndicator = UIActivityIndicatorView()
          activityIndicator.hidesWhenStopped = true
          activityIndicator.color = activityIndicatorColor
          return activityIndicator
      }
      
      private func showSpinning() {
          activityIndicator.translatesAutoresizingMaskIntoConstraints = false
          self.addSubview(activityIndicator)
          positionActivityIndicatorInButton()
          activityIndicator.startAnimating()
      }
      
      private func positionActivityIndicatorInButton() {
          let trailingConstraint = NSLayoutConstraint(item: self,
                                                     attribute: .trailing,
                                                     relatedBy: .equal,
                                                     toItem: activityIndicator,
                                                     attribute: .trailing,
                                                     multiplier: 1, constant: 16)
          self.addConstraint(trailingConstraint)
      
          let yCenterConstraint = NSLayoutConstraint(item: self,
                                                     attribute: .centerY,
                                                     relatedBy: .equal,
                                                     toItem: activityIndicator,
                                                     attribute: .centerY,
                                                     multiplier: 1, constant: 0)
          self.addConstraint(yCenterConstraint)
      }
      
      }
      

      如果您使用情节提要,请确保您的按钮是 LoadingButton 类型(在情节提要以及视图控制器文件上执行此操作,否则会崩溃)

      @IBOutlet weak var myButton: LoadingButton!
      

      并使用它,

      myButton.loadIndicator(true)
      

      【讨论】:

        【解决方案5】:

        斯威夫特 4:

        extension UIButton {
            func loadingIndicator(_ show: Bool) {
                let tag = 808404
                if show {
                    self.isEnabled = false
                    self.alpha = 0.5
                    let indicator = UIActivityIndicatorView()
                    let buttonHeight = self.bounds.size.height
                    let buttonWidth = self.bounds.size.width
                    indicator.center = CGPoint(x: buttonWidth/2, y: buttonHeight/2)
                    indicator.tag = tag
                    self.addSubview(indicator)
                    indicator.startAnimating()
                } else {
                    self.isEnabled = true
                    self.alpha = 1.0
                    if let indicator = self.viewWithTag(tag) as? UIActivityIndicatorView {
                        indicator.stopAnimating()
                        indicator.removeFromSuperview()
                    }
                }
            }
        }
        

        用法button.loadingIndicator(true/false)

        【讨论】:

          【解决方案6】:

          我用 swift 编写了一个库,它有 7 种不同的样式和动画来在 uibutton 中显示指示器视图,这里是

          https://github.com/farshadjahanmanesh/loady

          【讨论】:

            【解决方案7】:

            Swift4 的另一个解决方案,我试图让它比以前的解决方案更简单。此扩展允许您调整 UIActivityIndi​​catorView(缩放)的大小以适应 UIButton,并更改颜色。

            https://gist.github.com/jalopezsuarez/0ecc885b3fd5c555630799a067d66d98

            import Foundation
            import UIKit
            
            class UIButtonActivity: UIButton {
            
                @IBInspectable var indicatorColor : UIColor = .lightGray
            
                private var buttonLabel: String?
            
                func startAnimating() {
                    self.isEnabled = false
            
                    buttonLabel = self.titleLabel?.text
                    self.setTitle("", for: .normal)
            
                    let indicator = UIActivityIndicatorView()
                    indicator.color = indicatorColor
                    indicator.hidesWhenStopped = true
            
                    let buttonHeight = self.bounds.size.height
                    let buttonWidth = self.bounds.size.width
                    indicator.center = CGPoint(x: buttonWidth/2, y: buttonHeight/2)
            
                    let scale = max(min((self.frame.size.height - 4) / 21, 2.0), 0.0)
                    let transform: CGAffineTransform = CGAffineTransform(scaleX: scale, y: scale)
                    indicator.transform = transform
            
                    self.addSubview(indicator)
                    indicator.startAnimating()
                }
            
                func stopAnimating() {
                    self.isEnabled = true
            
                    if let titleLabel = buttonLabel {
                        self.setTitle(titleLabel, for: .normal)
                    }
            
                    if let indicator = self.viewWithTag(tag) as? UIActivityIndicatorView {
                        indicator.stopAnimating()
                        indicator.removeFromSuperview()
                    }
                }
            }
            

            【讨论】:

            • 这很好,但是在停止动画时你有 viewWithTag,没有真正分配标签。它可能对您有用,但我必须手动为指示器视图分配一个标签才能使其工作。干杯!
            【解决方案8】:

            @Boris:这不应该在扩展中。

            这里是 swift 3/4,改进了代码:禁用按钮,适用于图像和标题。

            class LoadingButton: UIButton {
            
                struct ButtonState {
                    var state: UIControlState
                    var title: String?
                    var image: UIImage?
                }
            
                private (set) var buttonStates: [ButtonState] = []
                private lazy var activityIndicator: UIActivityIndicatorView = {
                    let activityIndicator = UIActivityIndicatorView()
                    activityIndicator.hidesWhenStopped = true
                    activityIndicator.color = self.titleColor(for: .normal)
                    self.addSubview(activityIndicator)
                    activityIndicator.translatesAutoresizingMaskIntoConstraints = false
                    let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
                    let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
                    self.addConstraints([xCenterConstraint, yCenterConstraint])
                    return activityIndicator
                }()
            
                func showLoading() {
                    activityIndicator.startAnimating()
                    var buttonStates: [ButtonState] = []
                    for state in [UIControlState.disabled] {
                        let buttonState = ButtonState(state: state, title: title(for: state), image: image(for: state))
                        buttonStates.append(buttonState)
                        setTitle("", for: state)
                        setImage(UIImage(), for: state)
                    }
                    self.buttonStates = buttonStates
                    isEnabled = false
                }
            
                func hideLoading() {
                    activityIndicator.stopAnimating()
                    for buttonState in buttonStates {
                        setTitle(buttonState.title, for: buttonState.state)
                        setImage(buttonState.image, for: buttonState.state)
                    }
                    isEnabled = true
                }
            
            }
            

            【讨论】:

            • fyi,如果故事板中有一个按钮,则必须在身份检查器中更改其类型,而不仅仅是在代码中。
            • 如何使用这个LoadingButton,初始化时需要设置State、Title和Image吗?一个关于它的用法的例子会很棒。
            【解决方案9】:

            Swift 4.0 稍作修改

            class LoadingUIButton: UIButton {
            
                @IBInspectable var indicatorColor : UIColor = .lightGray
            
                var originalButtonText: String?
                var activityIndicator: UIActivityIndicatorView!
            
                func showLoading() {
                    originalButtonText = self.titleLabel?.text
                    self.setTitle("", for: .normal)
            
                    if (activityIndicator == nil) {
                        activityIndicator = createActivityIndicator()
                    }
            
                    showSpinning()
                }
            
                func hideLoading() {
                    DispatchQueue.main.async(execute: {
                        self.setTitle(self.originalButtonText, for: .normal)
                        self.activityIndicator.stopAnimating()
                    })
                }
            
                private func createActivityIndicator() -> UIActivityIndicatorView {
                    let activityIndicator = UIActivityIndicatorView()
                    activityIndicator.hidesWhenStopped = true
                    activityIndicator.color = indicatorColor
                    return activityIndicator
                }
            
                private func showSpinning() {
                    activityIndicator.translatesAutoresizingMaskIntoConstraints = false
                    self.addSubview(activityIndicator)
                    centerActivityIndicatorInButton()
                    activityIndicator.startAnimating()
                }
            
                private func centerActivityIndicatorInButton() {
                    let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
                    self.addConstraint(xCenterConstraint)
            
                    let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
                    self.addConstraint(yCenterConstraint)
                }
            
            }
            

            【讨论】:

              【解决方案10】:

              优秀的@史蒂夫,

              可以用下面的实现代替线,

              var activityIndi​​cator: UIActivityIndi​​catorView!

              使用它,这样如果我们在调用 showLoading() 之前配置活动指示器,应用程序就不会崩溃。

              private var _activityIndicator:UIActivityIndicatorView! = nil
              var activityIndicator: UIActivityIndicatorView{
                  set{
                      _activityIndicator = newValue
              
                  }
                  get{
                      if _activityIndicator == nil{
                          _activityIndicator = createActivityIndicator()
                      }
                      return _activityIndicator
                  }
              }
              

              【讨论】:

                【解决方案11】:

                更新了 Apple Swift 3.0.1 版 (swiftlang-800.0.58.6 clang-800.0.42.1) Xcode 8.1 (8B62) 的选定答案 (steve-rosenberg)

                import ObjectiveC
                
                private var originalButtonText: String?
                private var activityIndicator: UIActivityIndicatorView!
                
                extension UIButton{
                
                    func showLoading() {
                        originalButtonText = self.titleLabel?.text
                        self.setTitle("", for: .normal)
                
                        if (activityIndicator == nil) {
                            activityIndicator = createActivityIndicator()
                        }
                
                        showSpinning()
                    }
                
                    func hideLoading() {
                        self.setTitle(originalButtonText, for: .normal)
                        activityIndicator.stopAnimating()
                    }
                
                    private func createActivityIndicator() -> UIActivityIndicatorView {
                        let activityIndicator = UIActivityIndicatorView()
                        activityIndicator.hidesWhenStopped = true
                        activityIndicator.color = UIColor.lightGray
                        return activityIndicator
                    }
                
                    func showSpinning() {
                        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
                        self.addSubview(activityIndicator)
                        centerActivityIndicatorInButton()
                        activityIndicator.startAnimating()
                    }
                
                    private func centerActivityIndicatorInButton() {
                        let xCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: activityIndicator, attribute: .centerX, multiplier: 1, constant: 0)
                        self.addConstraint(xCenterConstraint)
                
                        let yCenterConstraint = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator, attribute: .centerY, multiplier: 1, constant: 0)
                        self.addConstraint(yCenterConstraint)
                    }
                }
                

                【讨论】:

                • originalButtonText 是全局变量,如果我们需要在多个按钮上显示加载器怎么办?
                • @NishuPriya 这是一个 UIButton 扩展,因此您可以将它单独用于每个按钮
                猜你喜欢
                • 1970-01-01
                • 2012-01-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-02-04
                • 2017-04-26
                相关资源
                最近更新 更多