2015-09-12 88 views
20

我尝试添加使用协议扩展,像这样一些UITextFieldDelegate的方法的默认行为:SWIFT 2.0 - UITextFieldDelegate协议扩展不工作

extension ViewController: UITextFieldDelegate { 
    // Works if I uncommented this so I know delegates are properly set 
// func textFieldShouldReturn(textField: UITextField) -> Bool { 
//  textField.resignFirstResponder() 
//  return true 
// } 
} 

extension UITextFieldDelegate { 
    func textFieldShouldReturn(textField: UITextField) -> Bool { 
     textField.resignFirstResponder() 

     return true 
    } 
} 

正如你可能已经猜到,键盘永不解散。我看不出这里的问题在哪里。这是语言限制吗?有人已经成功做到了吗?

编辑:

由于@Logan建议,默认协议的方法实现不标记为@objc协议工作。然而,UITextFieldDelegate具有以下特征public protocol UITextFieldDelegate : NSObjectProtocol {...}

我已经测试默认实施NSObjectProtocol,它似乎正常工作:

protocol Toto: NSObjectProtocol { 
    func randomInt() -> Int 
} 

extension Toto { 
    func randomInt() -> Int { 
     return 0 
    } 
} 

class Tata: NSObject, Toto {} 

let int = Tata().randomInt() // returns 0 

回答

8

我不能100%肯定的,但这里是我认为正在发生的事情:

ObjC无法访问协议扩展。由于UITextFieldDelegateObjC协议,因此它依赖于ObjC调度。就编译器而言,默认实现中的方法是不可访问的,尽管它们确实存在。

为了澄清,我们可以扩展这些协议,如果它的真正的扩展和添加行为。这种行为只能在Swift中访问,并且不应以任何方式出现问题。

问题是默认实现不是ObjC可访问。

下面是一个定制版本的一个简单的例子:

@objc protocol Test : class { 
    func someFunc() -> String 
} 

extension Test { 
    func someFunc() -> String { 
     return "" 
    } 
} 

// Fails here 'candidate is not @objc but protocol requires it` 
class Hi : NSObject, Test { 

} 

的Xcode提示追加@objc,但它会继续提示这一遍又一遍基于下面我们的谈话,直到你得到@objc @objc @objc Hi : ...

,我做了这似乎在工作。我不能完全解释为何尚未:

@objc public protocol Toto: UITextFieldDelegate { 
    optional func randomInt() -> Int 
} 

extension Toto { 
    func randomInt() -> Int { 
     return 0 
    } 
    func textFieldShouldReturn(textField: UITextField) -> Bool { 
     return false 
    } 
} 

class Tata: NSObject, Toto { 
} 

好吧,我意识到,我正在考虑一个不同的问题,虽然这编译,它不会工作,这个问题是动态调度。如果尝试附加方法w/@objcdynamic,编译器会警告您不能以这种方式调度,除了类。由于协议异常与此不符,因此当ObjC分派发送消息时,它无法在您的扩展中找到实现。

由于斯威夫特不断更新,这里是当这个答案是适用的:

雨燕2.0的Xcode 7 GM

+0

'UITextFieldDelegate'签名看起来像'公共协议UITextFieldDelegate:NSObjectProtocol {...}'。我只是测试协议的扩展来实现'NSObjectProtocol'协议,它似乎没有问题。此外,如果我试图扩展'@ obj'协议,编译器是不是应该警告我? – Yaman

+0

@Yaman - 我们都知道编译器的警告在Swift中并不完美,有时还不清楚。如果你超越“不符合”的范围,至少在我的系统中,我还会得到一个'@ objc'警告,它会一直添加'@ objc',但从未解决问题。这个问题并没有扩展ObjC协议,对于任何混淆感到抱歉。问题是创建依赖于ObjC可访问性的默认行为。只能在Swift中访问的ObjC协议的扩展和添加方法很好。添加必须是ObjC可访问的默认实现是导致问题的原因。 – Logan

+0

感谢这些精度。我也测试了实现'NSObjectProtocol'的协议的默认实现,并且也可以工作。是否声明'NSObjectProtocol'等价于'@ objc'标记?也许'UITextFieldDelegate'协议被隐式标记为'@ objc',但我的自定义协议实现'NSObjectProtocol'不? – Yaman

3

好这里的讨论,并且正是我在这一点上怀疑为好。 在这里没有提到另外一件事 - 可能这可能是由于obj-c无法访问Swift协议扩展实现的更广泛问题。

例如,下列代码:

class MyViewController: UIViewController, MyTextFieldDelegateProtocol { 
    @IBOutlet weak var textField: UITextField! 
    override func viewDidLoad() { 
     super.viewDidLoad() 
     textField.delegate = self 
    } 
} 
extension MyViewController: UITextFieldDelegate { 
    func textFieldDidBeginEditing(textField: UITextField) { 
     print("shouldChangeCharactersInRange called") 
    } 
} 

会生成在夫特生成的头下面为扩展:

@interface MyViewController (SWIFT_EXTENSION(MyApp)) <UITextFieldDelegate> 
- (void)textFieldDidBeginEditing:(UITextField * __nonnull)textField; 
@end 

然而,使用协议扩展如下(类似的交):

class MyViewController: UIViewController, MyTextFieldDelegateProtocol { 
    // ... 
} 

@objc protocol MyTextFieldDelegateProtocol: UITextFieldDelegate {} 
extension MyTextFieldDelegateProtocol { 
    func textFieldDidBeginEditing(textField: UITextField) { 
     print("shouldChangeCharactersInRange called") 
    } 
} 

在Swift标头中为p生成以下代码rotocol:

SWIFT_PROTOCOL("_TtP8MyApp27MyTextFieldDelegateProtocol_") 
@protocol MyTextFieldDelegateProtocol <UITextFieldDelegate> 
@end 

该实现根本不可见,所以它似乎暗示协议扩展实现不支持从obj-c。我也发现有人在这里问这个问题(虽然还没有答案): Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c

不幸的是,我还没有找到任何关于这个限制的官方苹果文档。