我正在创建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]
答案 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)
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))
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))