从协议扩展调用选择

问题描述:

我建立简单的主题引擎,并希望有一个扩展,它增加了UISwipeGestureRecognizerUIViewController从协议扩展调用选择

这里是我的代码:

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) 
    } 
} 

当然编译器无法找到#selector(Self.switchCurrentTheme)因为它没有通过@objc指令公开。是否有可能将此行为添加到我的扩展?

UPDATE:Theme是斯威夫特枚举,所以我不能在前面加@objcThemeable协议

我不知道,但你可以像

#selector(switchCurrentTheme) 

,或者你访问它可以参考此链接试试 参观How to use #selector(myMethodName) in a protocol extension?

+0

@OgreSwamp是它为你工作? –

+1

您的解决方案不起作用。您提供的链接解决方案也不适用于我。它描述了非常具体的情况 - 在方法调用之前等待。是的,你可以避免在那里使用objc目标/动作模式,但据我所知没有其他方式来添加手势监听器。 – OgreSwamp

我找到了解决方案。可能不是完美的,但它的工作原理。 由于我不能将Themeable协议定义为@objc,因为它使用Swift-only enum我决定移动要调用“父”协议的方法并将此协议定义为@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) 
    } 
} 
+1

仍然无法正常工作,因为我无法将'@ objc'应用于实现'Themeable'协议的'ViewController' .. – OgreSwamp

你有没有考虑建立一个包装,让你从一个@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) 
    } 
} 
+0

这是一个好主意,但无法让它工作。 :( –

最干净,工作解决方案,我能想出是确定与问题的方法上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) 
    } 
} 
+1

感谢你!工作就像一个魅力:)在Swift 3中,UIViewController的私有扩展现在需要是fileprivate。另外,switchCurrentTheme函数应该是fileprivate而不是内部。 –