视图控制器之间共享的UI功能

4

我有一些与UI相关的功能,例如显示警告弹窗,在屏幕中央呈现活动指示器,或者展示带有自定义消息的UIView动画。
我想在多个视图控制器(VC1和VC2)中使用它们,以避免重复代码。
最初,我有以下代码,可以让两个继承自BaseVC的VC都能使用这些功能。
VC1是一个带有嵌入式tableView的UIViewController,VC2是一个带有嵌入式collectionView的UIViewController。

class VC1: BaseVC {
    func viewDidAppear(animated: Bool) {
        activityIndicatorBegin()
    }

    func btnPressed() {
        activityIndicatorEnd()
    }
}

class VC2: BaseVC {
    func viewDidAppear(animated: Bool) {
        activityIndicatorBegin()
    }

    func btnPressed() {
        activityIndicatorEnd()
    }
}

class BaseVC: UIViewController {

    var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
    var isCustomViewOnScreen = false

    func activityIndicatorBegin() {
        if activityIndicator.isAnimating() == false {
            activityIndicator = UIActivityIndicatorView(frame: CGRectMake(0,0,20,20))
            activityIndicator.center = view.center
            activityIndicator.hidesWhenStopped = true
            activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge
            activityIndicator.color = UIColor.blackColor()
            view.addSubview(activityIndicator)
            activityIndicator.startAnimating()
        }
    }

    func activityIndicatorEnd() {
        if activityIndicator.isAnimating() == true {
            activityIndicator.stopAnimating()
            activityIndicator.removeFromSuperview()
        }
    }

    func animateACustomViewOnScreen() {
        if isCustomViewOnScreen == false {
            // Some animation code 
        }
    }

    func removeCustomView() {
        if isCustomViewOnScreen == true {
            // Some removal code
        }
    }
}

然而,由于某些原因,我决定让VC1直接继承UITableViewController,而将VC2从UICollectionViewController中继承。
这意味着它们不再能从UIViewController类的BaseVC继承了。

我该如何使得这两个VC仍然可以调用那些函数呢?

2个回答

4
答案是协议扩展。您可以将BaseVC定义为一个协议,然后通过添加共享逻辑来扩展它,这些逻辑将在实现它的类中使用:

(在下面的示例中,BaseVC被重命名为ActivityIndicatorDisplaying。)

protocol ActivityIndicatorDisplaying {

  var activityIndicator: UIActivityIndicatorView { get set }
  var showsCustomView: Bool { get }

  func showActivityIndicator()
  func dismissActivityIndicator()
}

extension ActivityIndicatorDisplaying where Self: UIViewController {

  func showActivityIndicator() {
    if activityIndicator.isAnimating() { return }

    activityIndicator.center = CGPointMake(view.bounds.width / 2, view.bounds.height / 2)
    activityIndicator.hidesWhenStopped = true
    activityIndicator.activityIndicatorViewStyle = .WhiteLarge
    activityIndicator.color = UIColor.blackColor()
    view.addSubview(activityIndicator)
    activityIndicator.startAnimating()
  }

  func dismissActivityIndicator() {
    activityIndicator.stopAnimating()
    activityIndicator.removeFromSuperview()
  }

  func animateACustomViewOnScreen() {
    if !showsCustomView {
      // Some animation code
    }
  }

  func removeCustomView() {
    if showsCustomView {
      // Some removal code
    }
  }
}

class VC1: UITableViewController, ActivityIndicatorDisplaying {

  var activityIndicator = UIActivityIndicatorView()
  var showsCustomView: Bool = false

  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    showActivityIndicator()
  }

  func btnPressed() {
    dismissActivityIndicator()
  }
}

class VC2: UICollectionViewController, ActivityIndicatorDisplaying {

  var activityIndicator = UIActivityIndicatorView()
  var showsCustomView: Bool = true

  ...
}

回答不错,但是如果你正在显示一个 UIViewextension ActivityIndicatorDisplaying where Self: UIViewController 仍然有效吗? - Prashant Tukadiya
不行,因为.addSubview是在视图控制器的.view属性上调用的。如果你想让它能够同时与UIView和UIViewController一起使用,那么你应该删除 where Self: UIViewController 并在协议中定义另一个属性,例如:var view: UIView { get } - Ozgur Vatansever
为什么这个解决方案比Jason下面的解决方案得到更多的投票?他的方法不会在所有VC中引入额外的标志。 - user125972

0
为什么不直接对UIActivityViewController进行子类化呢?这样更加合乎逻辑和清晰。
class VC1: UIViewController {
    var customActivityIndicatorView: CustomActivityIndicatorView? = nil

    func viewDidAppear(animated: Bool) {
        if let view = view {
            customActivityIndicatorView = CustomActivityIndicatorView(parentView: view)
        }
    }

    func btnPressed() {
        customActivityIndicatorView?.end()
    }
}

class VC2: UITableViewController {
    var customActivityIndicatorView: CustomActivityIndicatorView? = nil

    func viewDidAppear(animated: Bool) {
        if let view = view {
            customActivityIndicatorView = CustomActivityIndicatorView(parentView: view)
        }
    }

    func btnPressed() {
        customActivityIndicatorView?.end()
    }
}

class CustomActivityIndicatorView: UIActivityIndicatorView {
    var isCustomViewOnScreen = false

    convenience init?(parentView: UIView) {
        if isAnimating == false {
            self.init(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
            center = parentView.center
            hidesWhenStopped = true
            activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
            color = UIColor.black
            parentView.addSubview(self)
            startAnimating()
        } else {
            return nil
        }
    }

    func end() {
        if isAnimating == true {
            stopAnimating()
            removeFromSuperview()
        }
    }

    func animateACustomViewOnScreen() {
        if isCustomViewOnScreen == false {
            // Some animation code
        }
    }

    func removeCustomView() {
        if isCustomViewOnScreen == true {
            // Some removal code
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接