从协议扩展调用select器
我正在构build简单的主题引擎,并希望有一个扩展,它将UISwipeGestureRecognizer
添加到UIViewController
这是我的代码:
protocol Themeable { func themeDidUpdate(currentTheme: Theme) -> Void } extension Themeable where Self: UIViewController { func switchCurrentTheme() { Theme.switchTheme() themeDidUpdate(Theme.currentTheme) } func addSwitchThemeGestureRecognizer() { let gestureRecognizer = UISwipeGestureRecognizer(target: self, action:#selector(Self.switchCurrentTheme)) gestureRecognizer.direction = .Down gestureRecognizer.numberOfTouchesRequired = 2 self.view.addGestureRecognizer(gestureRecognizer) } }
当然编译器不能find#selector(Self.switchCurrentTheme)
因为它没有通过@objc
指令公开。 是否有可能将此行为添加到我的扩展?
更新: Theme
是一个Swift枚举,所以我不能在Themeable
协议前添加@objc
我可以想出最干净的工作解决scheme,就是在UIViewController
用所讨论的方法定义一个私有的扩展。 通过将范围限制为private
,对此方法的访问被隔离到定义协议的源文件中。下面是它的样子:
protocol Themeable { func themeDidUpdate(currentTheme: Theme) -> Void } private extension UIViewController { @objc func switchCurrentTheme() { guard let themeableSelf = self as? Themeable else { return } Theme.switchTheme() themeableSelf.themeDidUpdate(Theme.currentTheme) } } extension Themeable where Self: UIViewController { func addSwitchThemeGestureRecognizer() { let gestureRecognizer = UISwipeGestureRecognizer(target: self, action:#selector(switchCurrentTheme)) gestureRecognizer.direction = .Down gestureRecognizer.numberOfTouchesRequired = 2 self.view.addGestureRecognizer(gestureRecognizer) } }
我不确定,但你可以像访问它
#selector(switchCurrentTheme)
或者你可以参考这个链接来尝试访问如何在协议扩展中使用#selector(myMethodName)?
你有没有考虑创build一个包装,让你从@objc调用你的非@objc函数呢?
@objc class Wrapper: NSObject { let themeable: Themeable init(themeable: Themeable) { self.themeable = themeable } func switchCurrentTheme() { Theme.switchTheme() themeable.themeDidUpdate(Theme.currentTheme) } } protocol Themeable { func themeDidUpdate(currentTheme: Theme) -> Void } extension Themeable where Self: UIViewController { func addSwitchThemeGestureRecognizer() { let wrapper = Wrapper(themeable: self) let gestureRecognizer = UISwipeGestureRecognizer(target: wrapper, action:#selector(Wrapper.switchCurrentTheme)) gestureRecognizer.direction = .Down gestureRecognizer.numberOfTouchesRequired = 2 self.view.addGestureRecognizer(gestureRecognizer) } }
我find了解决scheme。 可能不是完美的,但它的工作原理。 由于我不能将Themeable
协议定义为@objc
因为它使用Swift-only enum
所以我决定移动我想调用“parent”协议的方法,并将此协议定义为@objc
。 这似乎是有效的,但我不太喜欢说实话…
@objc protocol ThemeSwitcher { func switchCurrentTheme() } protocol Themeable: ThemeSwitcher { func themeDidUpdate(currentTheme: Theme) -> Void } extension Themeable where Self: UIViewController { func switchCurrentTheme() { Theme.switchTheme() themeDidUpdate(Theme.currentTheme) } func addSwitchThemeGestureRecognizer() { let gestureRecognizer = UISwipeGestureRecognizer(target: self, action:#selector(switchCurrentTheme)) gestureRecognizer.direction = .Down gestureRecognizer.numberOfTouchesRequired = 2 self.view.addGestureRecognizer(gestureRecognizer) } }