iOS 一个易用的MaskableProtocol(beta)

// TODO: -开篇

  • 什么场景会遇到什么问题(这篇文章需要解决的)
  • 设计的原则
  • 解释过程
  • 总结

协议部分

public protocol MaskableProtocol: AnyObject {
    var maskableHelper: MaskableHelper { get set }
    func showMaskIfPossible()
    func hideMaskIfPossible()
}

协议的默认实现

private var gestureMaskViewKey: UInt8 = 0
private var maskableHelperKey: UInt8 = 0
public extension MaskableProtocol where Self: UIView {
    private var gestureMaskView: UIView? {
        get {
            return objc_getAssociatedObject(self, &gestureMaskViewKey) as? UIView
        }
        set {
            objc_setAssociatedObject(self, &gestureMaskViewKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    var maskableHelper: MaskableHelper {
        get {
            return objc_getAssociatedObject(self, &maskableHelperKey) as? MaskableHelper ?? MaskableHelper()
        }
        set {
            objc_setAssociatedObject(self, &maskableHelperKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    func showMaskIfPossible() {
        guard gestureMaskView == nil, let superview = self.superview else { return }
        UIView.performWithoutAnimation {
            gestureMaskView = UIView()
            gestureMaskView?.backgroundColor = UIColor(white: 0, alpha: 0.5)
            let tap = UITapGestureRecognizer(target: maskableHelper, action: #selector(maskableHelper.executeMaskAction))
            gestureMaskView?.addGestureRecognizer(tap)
            superview.insertSubview(gestureMaskView!, belowSubview: self)

            guard let maskView = gestureMaskView else { return }
            maskView.frame = superview.bounds
        }
    }

    func hideMaskIfPossible() {
        self.gestureMaskView?.removeFromSuperview()
        self.gestureMaskView = nil
    }
}

这里在extension中给UIView设置了两个关联对象,分别是:

  • gestureMaskView: UIView
    • 作用:添加到目标view的superview上,用于响应手势事件
  • maskableHelper: MaskableHelper
    • 作用:由于swift的extension中不支持运行时,无法直接使用Selector,需要借助一个类来中转Action

借助一个辅助类

public class MaskableHelper: NSObject {
    var maskAction: (() -> Void)?

    init(action: (() -> Void)? = nil) {
        self.maskAction = action
        super.init()
    }

    @objc func executeMaskAction() {
        maskAction?()
    }
}

应用

让需要作用的UIView遵守MaskableProtocol,并初始化一个MaskableHelper

class ContentInputView, MaskableProtocol {

    // MARK: - Lazy Loading
    internal lazy var maskableHelper: MaskableHelper = {
        let helper = MaskableHelper()
        helper.maskAction = { [weak self] in
            guard let `self` = self else { return }
            self.hideMaskIfPossible()
            self.endEditing(true)
        }
        return helper
    }()
} 

下需要显示Mask的时候调用协议中的func showMaskIfPossible( )即可

// MARK: - KeyboardObserving
func keyboardWillShow(notification: Notification) {
    self.replyInputView.showMaskIfPossible()
    if let height = notification.keyboardSize?.height, let duration = notification.keyboardAnimationDuration {
        UIView.animate(withDuration: duration) {
            self.replyInputView.willAscend()
            self.replyInputView.snp.updateConstraints { make in
                make.bottom.equalToSuperview().offset(-height)
            }
            self.view.layoutSubviews()
        }
    }
}

关键词

  • 关联对象
  • 协议
  • 协议的默认实现