Swift 2.0 - расширение UITextFieldDelegate не работает
Я пытаюсь добавить поведение по умолчанию для некоторых методов 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
}
}
Как вы можете догадаться, клавиатура никогда не увольняется. Я не могу понять, где эта проблема. Это ограничение языка? Кто-то уже успел это сделать?
EDIT:
Как показано в @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
Ответы
Ответ 1
Я не могу быть на 100% позитивным, но здесь, как я полагаю, происходит:
Расширения протокола недоступны из ObjC
. Поскольку UITextFieldDelegate
является протоколом ObjC
, он полагается на диспетчеризацию 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/ @objc
или dynamic
, компилятор предупредит вас, что вы не можете отправить этот путь, за исключением классов. Поскольку исключение протокола не соответствует этому, когда ObjC отправляет сообщение отправить, он не может найти реализацию в своем расширении.
Так как Swift постоянно обновляется, здесь, когда этот ответ применим:
Swift 2.0 Xcode 7 GM
Ответ 2
Хорошее обсуждение здесь, и именно то, что я подозреваю и в этот момент.
Еще одна вещь, не упомянутая здесь - возможно, это может быть связано с более широкой проблемой 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")
}
}
Сгенерирует следующее в заголовке Swift для расширения:
@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 для протокола:
SWIFT_PROTOCOL("_TtP8MyApp27MyTextFieldDelegateProtocol_")
@protocol MyTextFieldDelegateProtocol <UITextFieldDelegate>
@end
Реализация не видна вообще, и поэтому кажется, что реализация расширения протокола не поддерживается из obj-c. Я также нашел, что кто-то задал этот вопрос здесь (хотя ответов пока нет):
Может быть установлен метод Swift для расширений для протоколов, доступных в Objective-c
К сожалению, я еще не нашел официальных документов Apple об этом ограничении.