Foglio di azione con sovrapposizione della tastiera in iOS 13.1 su CNContactViewController


12

Questo sembra essere specifico di iOS 13.1, poiché funziona come previsto su iOS 13.0 e versioni precedenti per aggiungere un contatto in CNContactViewController, se I 'Annulla', il foglio di azione si sovrappone alla tastiera. Non vengono eseguite azioni e la tastiera non viene ignorata.

Risposte:


5

Complimenti a @GxocT per l'ottima soluzione! Aiutato immensamente i miei utenti.
Ma volevo condividere il mio codice basato sulla soluzione @GxocT sperando che potesse aiutare gli altri in questo scenario.

Avevo bisogno CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)di essere chiamato all'annullamento (oltre che fatto).

Inoltre il mio codice non era in un UIViewControllerquindi non c'èself.navigationController

Inoltre, non mi piace usare gli scarichi di forza quando posso evitarlo. Sono stato morso in passato, quindi ho incatenato if lets nel setup

Ecco cosa ho fatto:

  1. Estendi CNContactViewControllere posiziona la funzione swizzle
    .

  2. Nel mio caso nella funzione swizzle basta chiamare il
    CNContactViewControllerDelegatedelegato
    contactViewController(_:didCompleteWith:)con selfe l'
    self.contactoggetto dal controller di contatto

  3. Nel codice di installazione, assicurati che la chiamata swizzleMethod class_getInstanceMethodspecifichi la CNContactViewController classe anzichéself

E il codice 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)
    }
}

La tastiera mostra ancora momentaneamente, ma si abbassa subito dopo la chiusura del controller Contatti.
Speriamo che Apple risolva questo problema


forzare gli involucri sono usati per rendere il codice compatto, ovviamente dovresti evitarli se possibile e se ciò potrebbe portare a crash. tra l'altro non sono sicuro che sia corretto passare self.contact al delegato, perché probabilmente non è stato creato se si annulla il flusso ps: ha cambiato la mia implementazione per evitare forzature da scartare: D
GxocT

@GxocT - concordato sulla forza urwraps. E non sono sempre terribili, ma gli altri non sono come te e possono sempre usarli senza rendersi conto del rischio;). Mi piace se lascia invece che! quando non sono sicuro al 100% che non sarà zero. Informazioni su self.contact - è vero non so quale codice lib di Apple normalmente passi internamente al delegato ma self.contact aveva i dati di contatto e il documento CNContactViewController dice che è "il contatto visualizzato", quindi sembrava ok da usare. Il mio codice in realtà non utilizza il contatto passato nel delegato di completamento, quindi potrei semplicemente passare zero nell'estensione.
Barrett

I contenuti (comprese le visualizzazioni di testo e le tastiere) in CNContactViewController devono essere separati. Se è possibile utilizzare 'Visualizza gerarchia' in Xcode per questo controller di visualizzazione, è possibile che i contenuti non siano visibili. Di conseguenza, non possiamo controllare la tastiera né la visualizzazione del testo.
WildCat,

5

Non sono riuscito a trovare un modo per chiudere la tastiera. Ma almeno puoi far apparire ViewController usando il mio metodo.

  1. Non so perché, ma è impossibile chiudere la tastiera in CNContactViewController. Ho provato endEditing: crea il nuovo UITextField firstResponder e così via. Niente ha funzionato.
  2. Ho provato a modificare l'azione per il pulsante "Annulla". Puoi trovare questo pulsante nello stack di NavigationController, ma l'azione viene modificata ogni volta che digiti qualcosa.
  3. Finalmente ho usato il metodo frizzante. Non sono riuscito a trovare un modo per chiudere la tastiera, come ho detto prima, ma almeno puoi chiudere CNContactViewController quando viene premuto il pulsante "Annulla".
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)
        }
    }
}

PS: Puoi trovare ulteriori informazioni sull'argomento reddit: https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/


2

L'utente può infatti scorrere verso il basso per chiudere la tastiera, quindi toccare Annulla e vedere il foglio di azione. Quindi questo problema è deplorevole e sicuramente un bug (e ho presentato una segnalazione di bug) ma non fatale (anche se, per essere sicuro, la soluzione alternativa non è banale da scoprire per l'utente).

inserisci qui la descrizione dell'immagine


Potresti collegarti alla segnalazione di bug che hai presentato?
Paaske il


1

Grazie @Gxoct per l'ottimo lavoro svolto. Penso che questa sia una domanda e un post molto utili per coloro che stanno lavorando CNContactViewController. Ho avuto anche questo problema (fino ad ora) ma nell'obiettivo c. Interpreto il precedente codice Swift nell'obiettivo 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);
    }
}

Creazione di una CNContactViewControllercategoria per l'accesso al licenziamento;

@implementation CNContactViewController (Test)

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

@end

Ragazzi che non hanno molta familiarità con Swizzling, potete provare questo post di Matt


0

Grazie, @GxocT per la soluzione alternativa, tuttavia, la soluzione pubblicata qui è diversa da quella che hai pubblicato su Reddit.

Quello su Reddit funziona per me, questo no, quindi voglio ripubblicarlo qui. La differenza è sulla linea con SwizzledMethod che dovrebbe essere:

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

L'intero codice aggiornato è:

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)
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.