如何从任何ViewController轻松显示活动指示器?

时间:2018-04-19 20:13:30

标签: ios swift

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

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或某种多继承方法。

2 个答案:

答案 0 :(得分:2)

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
    }
}

答案 1 :(得分:1)

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

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

中使用任何视图