2015-09-12 8 views
20

Sto cercando di aggiungere un comportamento di default sui metodi un po UITextFieldDelegate s' utilizzando le estensioni di protocollo in questo modo:rapida 2.0 - protocollo di estensione UITextFieldDelegate non funziona

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

Come si può intuire, la tastiera non respingere. Non riesco davvero a vedere dov'è il problema qui. Si tratta di una limitazione della lingua? Qualcuno lo ha già fatto?

EDIT:

Come suggerito @Logan, implementazione del metodo di protocollo predefinito non funziona con i protocolli contrassegnate come @objc. Tuttavia, UITextFieldDelegate ha la seguente firma public protocol UITextFieldDelegate : NSObjectProtocol {...}

Sono implementazione predefinita di prova per NSObjectProtocol e sembra funziona bene:

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

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

class Tata: NSObject, Toto {} 

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

risposta

8

Non posso essere positivo al 100%, ma qui è quello che credo che sta accadendo :

Le estensioni del protocollo non sono accessibili da ObjC. Poiché UITextFieldDelegate è un protocollo ObjC, è dipendente dalla spedizione ObjC. Per quanto riguarda il compilatore, i metodi nell'implementazione predefinita sono inaccessibili, anche se esistono.

Per chiarire, possiamo estendere questi protocolli se è davvero un'estensione e aggiunge un comportamento. Questo comportamento sarà accessibile solo in Swift e non dovrebbe essere problematico in alcun modo.

Il problema è che le implementazioni predefinite non sono accessibili a ObjC.

Ecco un rapido esempio di una versione personalizzata:

@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 suggerisce aggiungendo @objc ma manterrà suggerendo questo più e più volte fino ad arrivare @objc @objc @objc Hi : ...

Sulla base della nostra conversazione di seguito, ho fatto questo sembra funzionare. Non posso pienamente spiegare perché ancora:

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

Ok, mi rendo conto che sto valutando un problema diverso, e mentre questo compila, non funzionerà, e la questione è dispatch dinamico. Se provi ad aggiungere il tuo metodo con @objc o dynamic, il compilatore ti avviserà che non puoi effettuare spedizioni in questo modo, ad eccezione delle classi. Poiché un'eccezione del protocollo non è conforme a questo, quando ObjC invia il messaggio di invio, non riesce a trovare l'implementazione nella propria estensione.

Dal Swift è in costante aggiornamento, ecco quando questa risposta era applicabile:

Swift 2.0 Xcode 7 GM

+0

La firma 'UITextFieldDelegate' si presenta come' protocollo pubblico UITextFieldDelegate: NSObjectProtocol {...} '. Ho appena testato l'estensione del protocollo per i protocolli che implementa 'NSObjectProtocol' e sembra non essere problematico. Inoltre, il compilatore non dovrebbe avvertirmi se provassi ad estendere i protocolli '@ obj'? – Yaman

+0

@Yaman - Sappiamo tutti che gli avvisi del compilatore non sono perfetti in Swift e talvolta non sono chiari. Se guardi oltre 'non è conforme ', almeno sul mio sistema, ricevo anche un avviso' @ objc' che continua ad aggiungere '@ objc', ma non risolve mai il problema. Il problema non è l'estensione di un protocollo ObjC, mi dispiace per ogni confusione. Il problema è la creazione di un comportamento predefinito che dipende dall'accessibilità ObjC. Estendere e aggiungere nuovi metodi a un protocollo ObjC accessibile solo in Swift va bene. L'aggiunta di implementazioni predefinite che devono essere accessibili da ObjC è ciò che causa il problema. – Logan

+0

Grazie per la precisione. Ho anche testato l'implementazione predefinita per il protocollo che implementa 'NSObjectProtocol' e funziona anche. Dichiarare 'NSObjectProtocol' equivale al segno' @ objc'? Forse il protocollo 'UITextFieldDelegate' è implicitamente marcato come' @ objc' ma il mio protocollo personalizzato che implementa 'NSObjectProtocol' non lo fa? – Yaman

3

buona discussione qui, e esattamente quello che sto ritenendo sospetto a questo punto pure. Un'altra cosa non menzionata qui - forse questo potrebbe essere dovuto a un problema più ampio di obj-c che non è in grado di accedere alle implementazioni dell'estensione del protocollo Swift.

Ad esempio, il seguente codice:

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

genererà il seguente nell'intestazione generato Swift per l'estensione:

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

Tuttavia, utilizzando le estensioni di protocollo come segue (simile al tuo post):

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

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

Genera quanto segue nell'intestazione Swift per la p ROTOCOLLO:

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

L'attuazione non è visibile a tutti, e così sembra implicare implementazioni estensione protocollo non sono supportati da obj-c. Ho anche trovato qualcuno che ha posto questa domanda (anche se non ci sono ancora risposte): Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c

Sfortunatamente, non ho ancora trovato documenti ufficiali Apple su questa limitazione.

Problemi correlati