web-dev-qa-db-ja.com

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

ご想像のとおり、キーボードが消えることはありません。ここで問題がどこにあるのか本当にわかりません。これは言語の制限ですか?誰かがすでにそれを成功させましたか?

編集:

@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
20
Yaman

私は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 {
}

わかりました。別の問題を検討していることに気付きました。これはコンパイルされますが、機能せず、問題は動的ディスパッチです。メソッドを@objcまたはdynamicで追加しようとすると、コンパイラは、クラスを除いて、この方法でディスパッチできないことを警告します。プロトコル例外がこれに準拠していないため、ObjCがメッセージ送信をディスパッチすると、拡張機能で実装が見つかりません。

Swiftは常に更新されているため、この回答が当てはまるのは次のとおりです。

Swift 2.0 Xcode 7 GM

9
Logan

ここでの良い議論、そしてまさにこの時点で私が疑っていること。ここで言及されていないもう1つのこと-おそらくこれは、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からサポートされていないことを意味しているようです。また、誰かがここでその質問をしているのを見つけました(まだ答えはありませんが): Can Swift Objective-cでアクセスされるプロトコルの拡張機能で定義されたメソッド

残念ながら、この制限に関する公式のAppleドキュメントはまだ見つかりません。

3
Ken Ko