如何从超类中调用子类方法与协议一致性

时间:2016-06-20 21:04:23

标签: ios swift inheritance swift2 swift-protocols

我正在创建swift UI组件,它将为UIView和UICollectionViewCell提供一些功能。我希望能够轻松定制。 (这里的代码当然是简化)在下面的代码中,我将在layoutSubview中做一些艰苦的工作(我必须为每个自定义类重写layoutSubviews,因为在扩展中我们不能覆盖类方法)和然后使用默认实现调用add方法,如果需要,该方法应该很容易改变其行为。

问题是正确创建SubClassView实例调用CustomView.layoutSubviews但是在此方法SomeProtocol.add内部调用扩展名而不是SubClassView.add。我知道这是通过Swift设计的,但是如何在不覆盖我为他准备的layoutSubviews的情况下实现我的组件的开发人员,仅覆盖我准备的一个add方法,用于使用默认实现进行自定义如果他不想改变它,那就为他做好准备。

protocol SomeProtocol: class {
    var property: Int { get set }
    func add() // Method that can be override for custom behaviour
}

extension SomeProtocol where Self: UIView {
    // Default implementation
    func add() {
        property += Int(frame.width)
        print("SomeProtocol.add [property: \(property)]")
    }
}

class CustomView: UIView, SomeProtocol {
    var property: Int = 1

    override func layoutSubviews() {
        super.layoutSubviews()
        print("CustomView.layoutSubviews")
        // Some specific high end code for CustomView
        add()
    }
}

class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
    var property: Int = 2

    override func layoutSubviews() {
        super.layoutSubviews()
        print("CustomCollectionViewCell.layoutSubviews")
        // Some specific high end code for CustomCollectionViewCell
        add()
    }

}

class SubClassView: CustomView { // Class that can implemented by the third party developer for customisation
    // This method will not be called
    func add() {
        property += 10
        print("SubClassView.add [property: \(property)]")
    }
}

let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
// prints:
// CustomView.layoutSubviews
// SomeProtocol.add [property: 101]

3 个答案:

答案 0 :(得分:1)

您可以在您的CustomView和SomeProtocol之间添加间接类,让我们将其命名为CustomViewBase。该类应继承UIView并实现SomeProtocol。现在创建CustomViewBase的类CustomView子类,并添加add()方法的实现并从中调用super.add()。

这是代码:

protocol SomeProtocol: class {
    var property: Int { get set }
    func add() // Method that can be override for custom behaviour
}

extension SomeProtocol where Self: UIView {
    // Default implementation
    func add() {
        property += Int(frame.width)
        print("SomeProtocol.add [property: \(property)]")
    }
}

class CustomViewBase: UIView, SomeProtocol {
    var property: Int = 1
}

class CustomView: CustomViewBase {
    func add() {
        super.add()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        print("CustomView.layoutSubviews 2")
        // Some specific high end code for CustomView
        add()
   }
}

class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
    var property: Int = 2

    override func layoutSubviews() {
        super.layoutSubviews()
        print("CustomCollectionViewCell.layoutSubviews")
        // Some specific high end code for CustomCollectionViewCell
        add()
    }    
}

class SubClassView: CustomView { // Class that can implemented by the     third party developer for customisation
    // This method will not be called
    override func add() {
        property += 10
        print("SubClassView.add [property: \(property)]")
    }
}

let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

答案 1 :(得分:0)

首先,让我们将所有动画内容移动到自己的东西中。

struct CustomAnimator {
    var touchesBegan: (UIView) -> Void = { $0.backgroundColor = .red() }
    var touchesEnded: (UIView) -> Void = { $0.backgroundColor = .clear() }
}

所以这就是我们想要动画的方式。触摸开始时,我们会让它们变红。当接触结束时,我们会让它们清楚。这只是一个例子,当然可以被覆盖;这些只是默认值。这可能不是最方便的数据结构;你可以想出很多其他方法来存储它。但关键是它只是一个动画结构,它会传递它传递的东西。它不是一个观点本身;没有子类化。它只是一个价值。

那么,我们怎么能用呢?在子类方便的情况下,我们可以这样使用它:

class CustomDownButton: UIButton {
    var customAnimator: CustomAnimator?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        customAnimator?.touchesBegan(self)
        super.touchesBegan(touches, with: event)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        customAnimator?.touchesEnded(self)
        super.touchesEnded(touches, with: event)
    }
}

因此,如果您想要对视图进行子类化,则可以只为其分配一个CustomAnimator,然后就可以执行此操作。但这并不是真正有趣的案例,IMO。有趣的情况是它的方便子类化。我们希望将其附加到普通按钮。

UIKit已经为我们提供了一种工具,可以将触摸行为附加到随机视图,而无需对其进行子类化。它被称为手势识别器。因此,让我们构建一个可以完成动画制作的动画:

class CustomAnimatorGestureRecognizer: UIGestureRecognizer {
    let customAnimator: CustomAnimator

    init(customAnimator: CustomAnimator) {
        self.customAnimator = customAnimator
        super.init(target: nil, action: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        if let view = view {
            customAnimator.touchesBegan(view)
        }
        super.touchesBegan(touches, with: event)
    }

    override func reset() {
        // I'm a bit surprised this isn't in the default impl.
        if self.state == .possible {
            self.state = .failed
        }
        super.reset()
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        if let view = view {
            customAnimator.touchesEnded(view)
        }
        self.reset()
        super.touchesEnded(touches, with: event)
    }
}

应该很清楚这是如何工作的。它只是调用动画师来修改我们的视图,无论何时触摸开始或停止。正确处理状态机需要一点点记账,但它非常简单。

没有第三步。设置它是微不足道的:

override func viewDidLoad() {
    super.viewDidLoad()
    customButton.customAnimator = CustomAnimator()
    normalButton.addGestureRecognizer(CustomAnimatorGestureRecognizer(customAnimator: CustomAnimator()))
}

GitHub上的完整项目。

答案 2 :(得分:0)

我玩这个,我想出了两种不同的方法。 (带游乐场的github - &gt; https://github.com/nonameplum/Swift-POP-with-Inheritance-problem

  1. 组合物
  2. struct BaseFunctionality<T: UIView where T: SomeProtocol> {
        var add: (T) -> Void
        init() {
            add = { (view) in
                view.property += Int(view.frame.width)
                print("BaseFunctionality [property: \(view.property)]")
            }
        }
    }
    
    protocol SomeProtocol: class {
        var property: Int { get set }
    }
    
    class CustomView: UIView, SomeProtocol {
        var property: Int = 1
        var baseFunc = BaseFunctionality<CustomView>()
    
        override func layoutSubviews() {
            super.layoutSubviews()
            print("CustomView.layoutSubviews")
            // Some specific high end code for CustomView
            baseFunc.add(self)
        }
    }
    
    class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
        var property: Int = 2
        var baseFunc = BaseFunctionality<CustomCollectionViewCell>()
    
        override func layoutSubviews() {
            super.layoutSubviews()
            print("CustomCollectionViewCell.layoutSubviews")
            // Some specific high end code for CustomCollectionViewCell
            baseFunc.add(self)
        }
    
    }
    
    class SubClassView: CustomView { // Class that can implemented by the third party developer for customisation
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            self.baseFunc.add = { (view) in
                view.property -= Int(view.frame.width)
                print("SubClassView [property: \(view.property)]")
            }
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
    }
    
    let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    
    1. 各种代表
    2. protocol Functionality: class {
          func add()
      }
      
      // Default implementation
      extension SomeProtocol where Self: UIView {
          func add() {
              property = -5
              print("Functionality.add [property = \(property)]")
          }
      }
      
      protocol SomeProtocol: class {
          var property: Int { get set }
          var delegate: Functionality? { get set }
      }
      
      class CustomView: UIView, SomeProtocol {
          var property: Int = 1
          weak var delegate: Functionality?
      
          override func layoutSubviews() {
              super.layoutSubviews()
              print("CustomView.layoutSubviews")
              // Some specific high end code for CustomView
              if let delegate = delegate {
                  delegate.add()
              } else {
                  self.add()
              }
          }
      }
      
      class CustomCollectionViewCell: UICollectionViewCell, SomeProtocol {
          var property: Int = 2
          weak var delegate: Functionality?
      
          override func layoutSubviews() {
              super.layoutSubviews()
              print("CustomCollectionViewCell.layoutSubviews")
              // Some specific high end code for CustomCollectionViewCell
              if let delegate = delegate {
                  delegate.add()
              } else {
                  self.add()
              }
          }
      }
      
      class SubClassView: CustomView { // Class that can implemented by the third party developer for customisation
          override init(frame: CGRect) {
              super.init(frame: frame)
              self.delegate = self
          }
      
          required init?(coder aDecoder: NSCoder) {
              fatalError("init(coder:) has not been implemented")
          }
      }
      
      extension SubClassView: Functionality {
          func add() {
              property = 5
              print("SubClassView.add [property = \(property)]")
          }
          func doNothing() { print("SubClassView.doNothing [\(property)]") }
      }
      
      let view = SubClassView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))