Hoja de acción de superposición de teclado en iOS 13.1 en CNContactViewController

12

Esto parece ser específico para iOS 13.1, ya que funciona como se esperaba en iOS 13.0 y versiones anteriores para agregar un contacto en CNContactViewController, si 'Cancelar', la hoja de acción se superpone con el teclado. No se realizan acciones y el teclado no se descarta.

mounika 582
fuente

Respuestas:

5

¡Felicitaciones a @GxocT por la gran solución! Ayudé a mis usuarios inmensamente.
Pero quería compartir mi código basado en la solución @GxocT con la esperanza de que ayude a otros en este escenario.

Necesitaba que me CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)llamaran para cancelar (y listo).

Además, mi código no estaba en un, UIViewControllerasí que no hayself.navigationController

Tampoco me gusta usar desenvolturas forzadas cuando puedo evitarlo. He sido mordido en el pasado, así que encadené if lets en la configuración

Esto es lo que hice:

  1. Extienda CNContactViewControllery coloque la función swizzle
    allí.

  2. En mi caso, en la función swizzle simplemente llame al
    CNContactViewControllerDelegatedelegado
    contactViewController(_:didCompleteWith:)con selfy
    self.contactobjeto desde el controlador de contacto

  3. En el código de configuración, asegúrese de que la llamada swizzleMethod class_getInstanceMethodespecifique la CNContactViewController clase en lugar deself

Y el código Swift:

class MyClass: CNContactViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.changeImplementation()
    }

    func changeCancelImplementation() {

        let originalSelector = Selector(("editCancel:"))
        let swizzledSelector = #selector(CNContactViewController.cancelHack)

        if let originalMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), originalSelector),
           let swizzledMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), swizzledSelector) {

            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }

   func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
       // dismiss the contacts controller as usual
       viewController.dismiss(animated: true, completion: nil)
       // do other stuff when your contact is canceled or saved
       ...
    }
}

extension CNContactViewController {
    @objc func cancelHack()  {
        self.delegate?.contactViewController?(self, didCompleteWith: self.contact)
    }
}

El teclado todavía se muestra momentáneamente pero se cae justo después de que el controlador de Contactos se desconecta.
Esperemos que Apple arregle esto

Barrett
fuente
los desenvolvimientos forzados se utilizan para hacer el código compacto, por supuesto, debe evitarlos si es posible y si puede provocar un bloqueo. por cierto, no estoy seguro de si es correcto pasar self.contact para delegar, porque probablemente no se crea si cancela el flujo ps: cambió mi implementación para evitar desenredos
forzados
@GxocT - acordado en la fuerza urwraps. Y no siempre son terribles, pero otros no son como usted y siempre pueden usarlos sin darse cuenta del riesgo;). Me gusta si deja en lugar de! cuando no estoy 100% seguro de que no será nulo. Acerca de self.contact: es cierto. No sé qué código lib de Apple normalmente pasa al delegado internamente, pero self.contact tenía los datos de contacto y el documento CNContactViewController dice que es "el contacto que se muestra", por lo que parecía correcto usarlo. Mi código en realidad no usa el contacto pasado en el delegado de finalización, por lo que podría pasar cero en la extensión.
Barrett
El contenido (incluyendo vistas de texto y teclados) en CNContactViewController debe estar en un proceso separado. Si puede usar 'Ver jerarquía' en Xcode para este controlador de vista, puede encontrar que el contenido no se puede ver. Como resultado, no podemos controlar el teclado ni la vista de texto.
WildCat
5

No pude encontrar una manera de descartar el teclado. Pero al menos puedes hacer estallar ViewController usando mi método.

  1. No sé por qué, pero es imposible descartar el teclado en CNContactViewController. Intenté endEditing :, hacer nuevo UITextField firstResponder y así sucesivamente. Nada funcionó.
  2. Traté de alterar la acción para el botón "Cancelar". Puede encontrar este botón en la pila NavigationController, pero su acción cambia cada vez que escribe algo.
  3. Finalmente utilicé el método swizzling. No pude encontrar una manera de descartar el teclado como mencioné anteriormente, pero al menos puede descartar CNContactViewController cuando se presiona el botón "Cancelar".
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        changeImplementation()
    }

    @IBAction func userPressedButton(_ sender: Any) {
        let controller = CNContactViewController(forNewContact: nil)
        controller.delegate = self
        navigationController?.pushViewController(controller, animated: true)
    }

    @objc func popController() {
        self.navigationController?.popViewController(animated: true)
    }

    func changeImplementation() {
        let originalSelector = Selector("editCancel:")
        let swizzledSelector = #selector(self.popController)

        if let originalMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), originalSelector),
            let swizzledMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), swizzledSelector) {

            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
}

PD: Puede encontrar información adicional sobre el tema de reddit: https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/

GxocT
fuente
2

De hecho, el usuario puede deslizar hacia abajo para cerrar el teclado y luego tocar Cancelar y ver la hoja de acción. Por lo tanto, este problema es lamentable y definitivamente es un error (y he presentado un informe de error) pero no fatal (aunque, para estar seguro, la solución no es trivial para que el usuario la descubra).

ingrese la descripción de la imagen aquí

mate
fuente
¿Podría vincular al informe de error que presentó?
Paaske
1

Corregido en iOS 13.4 Probado en Xcode Simulator

Peretz30
fuente
1

Gracias @Gxoct por su excelente trabajo. Creo que esta es una pregunta y publicación muy útil para quienes están trabajando CNContactViewController. También tuve este problema (hasta ahora) pero en el objetivo c. Interpreto el código Swift anterior en el objetivo c.

- (void)viewDidLoad {
    [super viewDidLoad];
    Class class = [CNContactViewController class];

    SEL originalSelector = @selector(editCancel:);
    SEL swizzledSelector = @selector(dismiss); // we will gonna access this method & redirect the delegate via this method

    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    BOOL didAddMethod =
        class_addMethod(class,
            originalSelector,
            method_getImplementation(swizzledMethod),
            method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
            swizzledSelector,
            method_getImplementation(originalMethod),
            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

Crear una CNContactViewControllercategoría para acceder al despido;

@implementation CNContactViewController (Test)

- (void) dismiss{
    [self.delegate contactViewController:self didCompleteWithContact:self.contact];
}

@end

Chicos que no están tan familiarizados con Swizzling, pueden probar esta publicación de matt

Sk Borhan Uddin
fuente
0

Gracias, @GxocT por su solución, sin embargo, la solución publicada aquí es diferente de la que publicó en Reddit.

El de Reddit funciona para mí, este no lo hace, así que quiero volver a publicarlo aquí. La diferencia está en la línea con swizzledMethod, que debería ser:

   let swizzledMethod = class_getInstanceMethod(object_getClass(self), swizzledSelector) {

Todo el código actualizado es:

class MyClass: CNContactViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.changeImplementation()
    }

    func changeCancelImplementation() {

        let originalSelector = Selector(("editCancel:"))
        let swizzledSelector = #selector(CNContactViewController.cancelHack)

        if let originalMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), originalSelector),
           let swizzledMethod = class_getInstanceMethod(object_getClass(self), swizzledSelector) {

            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }

   func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
       // dismiss the contacts controller as usual
       viewController.dismiss(animated: true, completion: nil)
       // do other stuff when your contact is canceled or saved
       ...
    }
}

extension CNContactViewController {
    @objc func cancelHack()  {
        self.delegate?.contactViewController?(self, didCompleteWith: self.contact)
    }
}
jakub1984
fuente