在 swift 中重写受约束为协议的类扩展中的方法



我正在尝试将默认实现添加到UIView控制器触摸开始到所有通过协议扩展符合协议的控制器。在那里,触摸将被发送到所有实现此协议的控制器都具有的自定义视图。

以下是初始状态:

protocol WithView {
var insideView: UIView! { get }
}
class Controller1: UIViewController, WithView {
var insideView: UIView!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
insideView.touchesBegan(touches, with: event)
}
/* Functionality of Controller 1 */
}
class Controller2: UIViewController, WithView {
var insideView: UIView!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
insideView.touchesBegan(touches, with: event)
}
/* Functionality of Controller 2 */
}

我想完成的情况是,所有UIViewControllers都将触摸转发到insideView,而不以相同的方式为每个控制器指定。像这样:

protocol WithView {
var insideView: UIView! { get }
}
extension UIViewController where Self: WithView {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
insideView.touchesBegan(touches, with: event)
}
}
class Controller1: UIViewController, WithView {
var insideView: UIView!
/* Functionality of Controller 1 */
}
class Controller2: UIViewController, WithView {
var insideView: UIView!
/* Functionality of Controller 2 */
}

但这不能编译,说"尾随where子句用于扩展非泛型类型UIViewController">

我试图以相反的方式定义它,如下所示:

extension WithView where Self: UIViewController {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
insideView.touchesBegan(touches, with: event)
}
}

虽然扩展的格式正确,但编译器会抱怨,因为它无法"覆盖"协议扩展中的内容。

我想要的是一个受协议约束的类扩展,例如我可以重写此方法,而不是被迫将代码复制粘贴到实现此协议的所有控制器中。

编辑:根据建议的解决方案

我还想出了这个解决方案:

protocol WithView {
var insideView: UIView! { get }
}
extension UIViewController {
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let viewSelf = (self as? WithView) else {
super.touchesBegan(touches, with: event)
return
}
viewSelf.insideView.touchesBegan(touches, with: event)
}
}
class Controller1: UIViewController, WithView {
var insideView: UIView!
/* Functionality of Controller 1 */
}
class Controller2: UIViewController, WithView {
var insideView: UIView!
/* Functionality of Controller 2 */
}

它做了我想要的,但它感觉有点混乱,因为这样所有的 UIViewControllers 都会改变这种行为,并会覆盖其代码,检查它们是否实现了协议。

您可以为所有视图控制器定义自己的超类,并检查self是否符合特定协议(在您的情况下WithView),以决定是否应将触摸事件转发到任何其他视图。

class MyViewController: UIViewController {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let selfWithView = self as? WithView {
selfWithView.insideView.touchesBegan(touches, with: event)
} else {
super.touchesBegan(touches, with: event)
}
}
}

这是一种更灵活的方法,您不必在每个视图控制器子类中存储insideView属性。

您可以通过创建一个类并从中子类化来做到这一点:

class WithViewController: UIViewController, WithView {
var insideView: UIView!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
insideView.touchesBegan(touches, with: event)
}
}
class ViewController: WithViewController {
}

唯一的缺点是你必须有一个默认的insideView它永远不会被改变。

最新更新