Подтвердить что ты не робот

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
4b9b3361

Ответ 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 об этом ограничении.