【问题标题】:How to easily display an activity indicator from any ViewController?如何从任何 ViewController 轻松显示活动指示器?
【发布时间】:2018-04-19 20:13:29
【问题描述】:

编写一个包含一些网络活动的应用程序我发现自己一遍又一遍地为多个视图控制器编写相同的代码,只是为了显示一个活动指示器。

class SomeViewController: UIViewController {
    let indicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))

    override func viewDidLoad() {
        super.viewDidLoad()

        // customize indicator
        self.indicator.layer.cornerRadius = 10
        self.indicator.center = self.view.center
        self.indicator.hidesWhenStopped = true
        self.indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
        self.indicator.backgroundColor = UIColor(red: 1/255, green: 1/255, blue: 1/255, alpha: 0.5)
    }
    // MARK: - Acitivity Indicator
    func startIndicatingActivity() {
        DispatchQueue.main.async {
            self.view.addSubview(self.indicator)
            self.indicator.startAnimating()
        }
    }

    func stopIndicatingActivity() {
        DispatchQueue.main.async {
            self.indicator.stopAnimating()
        }
    }
}

在同一个 SomeViewController 类中,我可以按如下方式使用它:

@IBAction func startHeavyNetworkStuffButtonPressed(_ sender: UIButton) {
    startIndicatingActivity()
    doHeavyNetworkStuff() { success in
        // heavy networking has finished
        stopIndicatingActivity()
    }
}

只要我只需要在单个视图控制器中显示活动指示器,它就可以正常工作。但是,为每个需要此功能的视图控制器一遍又一遍地执行此操作很乏味。由于我讨厌一遍又一遍地编写相同的代码,我正在寻找一个我可以简单地调用的解决方案 任何视图控制器中的startIndicatingActivity()(和stopIndicatingActivity())。

第 0 个想法 - 扩展

我的第一个想法显然是为UIViewController 类编写一个扩展。但是,由于我需要存储UIActivityIndicatorView 的实例,我得到了Extensions may not contain stored properties 错误。

第一个想法 - 子类化

接下来:子类化UIViewController。这对于任何简单的视图控制器都可以正常工作。但是,如果我需要为 MyCustomTableViewController 提供相同的功能,我将再次需要首先从 UITableViewController 继承并复制/粘贴现有​​代码

我的问题

是否有一种优雅的方式可以在任何视图控制器中调用startIndicatingActivity() / stopIndicatingActivity(),同时避免复制/粘贴大量代码?我假设一个优雅的解决方案将涉及 extensionprotocol 或某种多重继承方法。

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    This SO thread 是解决方案!毕竟,一种方法可以通过extension模拟属性来解决这个问题。

    为感兴趣的读者发布完整的解决方案:

    扩展UIViewController

    extension UIViewController {
        // see ObjectAssociation<T> class below
        private static let association = ObjectAssociation<UIActivityIndicatorView>()
    
        var indicator: UIActivityIndicatorView {
            set { UIViewController.association[self] = newValue }
            get {
                if let indicator = UIViewController.association[self] {
                    return indicator
                } else {
                    UIViewController.association[self] = UIActivityIndicatorView.customIndicator(at: self.view.center)
                    return UIViewController.association[self]!
                }
            }
        }
    
        // MARK: - Acitivity Indicator
        public func startIndicatingActivity() {
            DispatchQueue.main.async {
                self.view.addSubview(self.indicator)
                self.indicator.startAnimating()
                //UIApplication.shared.beginIgnoringInteractionEvents() // if desired
            }
        }
    
        public func stopIndicatingActivity() {
            DispatchQueue.main.async {
                self.indicator.stopAnimating()
                //UIApplication.shared.endIgnoringInteractionEvents()
            }
        }
    }
    

    借用上述SO thread的代码

    // source: https://stackoverflow.com/questions/25426780/how-to-have-stored-properties-in-swift-the-same-way-i-had-on-objective-c
    public final class ObjectAssociation<T: AnyObject> {
    
        private let policy: objc_AssociationPolicy
    
        /// - Parameter policy: An association policy that will be used when linking objects.
        public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
    
            self.policy = policy
        }
    
        /// Accesses associated object.
        /// - Parameter index: An object whose associated object is to be accessed.
        public subscript(index: AnyObject) -> T? {
    
            get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
            set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
        }
    }
    

    为了完整起见

    我还使用我的自定义扩展了 UIActivityIndicatorView 类的静态函数。

    extension UIActivityIndicatorView {
        public static func customIndicator(at center: CGPoint) -> UIActivityIndicatorView {
            let indicator = UIActivityIndicatorView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
            indicator.layer.cornerRadius = 10
            indicator.center = center
            indicator.hidesWhenStopped = true
            indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
            indicator.backgroundColor = UIColor(red: 1/255, green: 1/255, blue: 1/255, alpha: 0.5)
            return indicator
        }
    }
    

    【讨论】:

      【解决方案2】:

      在任何视图中显示完整加载的示例

      extension UIView{
       /**
           ShowLoader:  loading view ..
      
           - parameter Color:  ActivityIndicator and view loading color .
      
           */
          func showLoader(_ color:UIColor?){
      
      
              let LoaderView  = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height))
              LoaderView.tag = -888754
              LoaderView.backgroundColor = color
              let Loader = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 60, height: 30))
              Loader.center = LoaderView.center
              Loader.activityIndicatorViewStyle = .whiteLarge
              Loader.color = Color.primaryColor
              Loader.startAnimating()
              LoaderView.addSubview(Loader)
              self.addSubview(LoaderView)
          }
      
       /**
           dismissLoader:  hidden loading view  ..     
           */
          func dismissLoader(){
      
      
              self.viewWithTag(-888754)?.removeFromSuperview()
          }
      }
      

      调用这个函数

      UIViewController

      self.view.showLoader(nil) //you can set background color nil or any color
      // dismiss
      self.view.dismissLoader()
      

      我更喜欢这种方法,因为您可以在button , tableview , cell ...etc 中的任何视图中使用它

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-28
        • 1970-01-01
        • 1970-01-01
        • 2012-01-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多