ios - shouldchangecharactersin - textfielddidchange swift 3
swift 2.0-La extensión del protocolo UITextFieldDelegate no funciona (2)
Estoy tratando de agregar un comportamiento predeterminado en algunos métodos de UITextFieldDelegate
usando extensiones de protocolo así:
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
}
}
Como puedes adivinar, el teclado nunca se despide. Realmente no puedo ver dónde está el problema aquí. ¿Es esta una limitación del idioma? ¿Alguien ya lo hizo con éxito?
EDITAR:
Como sugirió @Logan, la implementación del método del protocolo predeterminado no funciona con los protocolos marcados como @objc
. Sin embargo, UITextFieldDelegate
tiene la siguiente firma de public protocol UITextFieldDelegate : NSObjectProtocol {...}
He probado la implementación predeterminada para NSObjectProtocol
y parece que funciona bien:
protocol Toto: NSObjectProtocol {
func randomInt() -> Int
}
extension Toto {
func randomInt() -> Int {
return 0
}
}
class Tata: NSObject, Toto {}
let int = Tata().randomInt() // returns 0
Buena discusión aquí, y exactamente lo que estoy sospechando en este punto también. Otra cosa que no se menciona aquí, tal vez se deba a un problema más amplio de que obj-c no puede acceder a las implementaciones de la extensión del protocolo Swift.
Por ejemplo, el siguiente código:
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")
}
}
Generará lo siguiente en el encabezado Swift generado para la extensión:
@interface MyViewController (SWIFT_EXTENSION(MyApp)) <UITextFieldDelegate>
- (void)textFieldDidBeginEditing:(UITextField * __nonnull)textField;
@end
Sin embargo, utiliza las extensiones de protocolo de la siguiente manera (similar a tu publicación):
class MyViewController: UIViewController, MyTextFieldDelegateProtocol {
// ...
}
@objc protocol MyTextFieldDelegateProtocol: UITextFieldDelegate {}
extension MyTextFieldDelegateProtocol {
func textFieldDidBeginEditing(textField: UITextField) {
print("shouldChangeCharactersInRange called")
}
}
Genera lo siguiente en el encabezado Swift para el protocolo:
SWIFT_PROTOCOL("_TtP8MyApp27MyTextFieldDelegateProtocol_")
@protocol MyTextFieldDelegateProtocol <UITextFieldDelegate>
@end
La implementación no es visible en absoluto, por lo que parece implicar que las implementaciones de extensión de protocolo no son compatibles con obj-c. También encontré a alguien que hizo esa pregunta aquí (aunque todavía no hay respuestas): ¿Se puede cambiar el método definido en extensiones en protocolos a los que se accede en Objective-c?
Desafortunadamente, todavía no he encontrado ningún documento oficial de Apple sobre esta limitación.
No puedo ser 100% positivo, pero esto es lo que creo que está sucediendo:
Las extensiones de protocolo no son accesibles desde ObjC
. Dado que UITextFieldDelegate
es un protocolo ObjC
, depende del envío de ObjC
. En lo que respecta al compilador, los métodos en su implementación por defecto son inaccesibles, aunque existan.
Para aclarar, podemos extender estos protocolos si es realmente una extensión y agrega comportamiento. Este comportamiento solo será accesible en Swift y no debería ser problemático de ninguna manera.
El problema es que las implementaciones predeterminadas no son accesibles a ObjC
.
Aquí hay un ejemplo rápido de una versión personalizada:
@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 sugiere @objc
pero seguirá sugiriendo esto una y otra vez hasta que obtengas @objc @objc @objc Hi : ...
Basándome en nuestra conversación a continuación, hice esto que parece estar funcionando. Todavía no puedo explicar completamente por qué:
@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, me doy cuenta de que estoy considerando un problema diferente, y mientras esto se compila, no funcionará, y el problema es el envío dinámico. Si intenta agregar su método w / @objc
, o dynamic
, el compilador le advertirá que no puede enviar de esta manera, excepto en las clases. Como una excepción de protocolo no se ajusta a esto, cuando ObjC envía el mensaje enviado, no puede encontrar la implementación en su extensión.
Como Swift se actualiza constantemente, aquí es cuando esta respuesta era aplicable:
Swift 2.0 Xcode 7 GM