Feuille d'action de superposition de clavier dans iOS 13.1 sur CNContactViewController


12

Cela semble être spécifique à iOS 13.1, car cela fonctionne comme prévu sur iOS 13.0 et les versions antérieures pour ajouter un contact dans CNContactViewController, si je `` Annuler '', la feuille d'action se chevauche par clavier. Aucune action n'est effectuée et le clavier ne se ferme pas.

Réponses:


5

Félicitations à @GxocT pour l'excellente solution de contournement! J'ai énormément aidé mes utilisateurs.
Mais je voulais partager mon code basé sur la solution @GxocT en espérant qu'il aiderait les autres dans ce scénario.

J'avais besoin que mon CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)appel soit annulé (ainsi que fait).

De plus, mon code n'était pas dans un UIViewControllerdonc il n'y a pasself.navigationController

Je n'aime pas non plus utiliser les déballages de force quand je peux l'aider. J'ai été mordu dans le passé donc j'ai enchaîné les if lets dans la configuration

Voici ce que j'ai fait:

  1. Étendez CNContactViewControlleret placez la fonction swizzle à l'intérieur
    .

  2. Dans mon cas, dans la fonction swizzle, il suffit d'appeler le
    CNContactViewControllerDelegatedélégué
    contactViewController(_:didCompleteWith:)avec selfet l'
    self.contactobjet du contrôleur de contact

  3. Dans le code d'installation, assurez-vous que l'appel swizzleMethod class_getInstanceMethodspécifie la CNContactViewController classe au lieu deself

Et le code 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)
    }
}

Le clavier s'affiche toujours momentanément, mais disparaît juste après que le contrôleur de contacts a quitté.
Espérons qu'Apple corrige ce problème


les déballages forcés sont utilisés pour rendre le code compact bien sûr, vous devez les éviter si possible et si cela peut conduire à un crash. btw je ne sais pas s'il est correct de passer self.contact à déléguer, car il n'est probablement pas créé si vous annulez le flux ps: a changé mon implémentation pour éviter les
déballages forcés

@GxocT - a accepté les urwraps de force. Et ils ne sont pas toujours terribles mais d'autres ne vous ressemblent pas et peuvent toujours les utiliser sans se rendre compte du risque;). J'aime si on laisse au lieu de! quand je ne suis pas sûr à 100%, ce ne sera pas nul. À propos de self.contact - c'est vrai, je ne sais pas ce que le code lib d'Apple transmet normalement au délégué en interne, mais self.contact avait les données de contact et le document CNContactViewController dit que c'est "le contact affiché", donc il semblait correct à utiliser. Mon code n'utilise pas réellement le contact passé dans le délégué d'achèvement afin que je puisse simplement passer zéro dans l'extension.
Barrett

Le contenu (y compris les vues de texte et les claviers) de CNContactViewController doit faire l'objet d'un processus distinct. Si vous pouvez utiliser «Afficher la hiérarchie» dans Xcode pour ce contrôleur de vue, il se peut que le contenu ne soit pas visible. Par conséquent, nous ne pouvons pas contrôler le clavier ni la vue texte.
WildCat

5

Je n'ai pas trouvé de moyen de rejeter le clavier. Mais au moins, vous pouvez faire apparaître ViewController en utilisant ma méthode.

  1. Je ne sais pas pourquoi mais il est impossible de supprimer le clavier dans CNContactViewController. J'ai essayé endEditing:, faire un nouveau premier répondeur UITextField et ainsi de suite. Rien n'a fonctionné.
  2. J'ai essayé de modifier l'action du bouton "Annuler". Vous pouvez trouver ce bouton dans la pile NavigationController, mais son action est modifiée à chaque fois que vous tapez quelque chose.
  3. Enfin, j'ai utilisé la méthode swizzling. Je n'ai pas pu trouver un moyen de fermer le clavier comme je l'ai mentionné plus tôt, mais au moins vous pouvez fermer CNContactViewController lorsque vous appuyez sur le bouton "Annuler".
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: Vous pouvez trouver des informations supplémentaires sur le sujet reddit: https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/


2

L'utilisateur peut en fait glisser vers le bas pour fermer le clavier, puis appuyez sur Annuler et voir la feuille d'action. Ce problème est donc regrettable et certainement un bogue (et j'ai déposé un rapport de bogue) mais pas fatal (bien que, pour être sûr, la solution de contournement ne soit pas triviale pour l'utilisateur à découvrir).

entrez la description de l'image ici


Pourriez-vous créer un lien vers le rapport de bogue que vous avez déposé?
Paaske


1

Merci @Gxoct pour son excellent travail. Je pense que c'est une question et un message très utiles pour ceux qui travaillent avec CNContactViewController. J'ai aussi eu ce problème (jusqu'à présent) mais dans l'objectif c. J'interprète le code Swift ci-dessus dans l'objectif 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);
    }
}

Création d'une CNContactViewControllercatégorie pour accéder à licencier;

@implementation CNContactViewController (Test)

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

@end

Les gars qui ne sont pas si familiers avec Swizzling, vous pouvez essayer ce post par matt


0

Merci, @GxocT pour votre solution de contournement, cependant, la solution publiée ici est différente de celle que vous avez publiée sur Reddit.

Celui sur Reddit fonctionne pour moi, celui-ci ne fonctionne pas, donc je veux le republier ici. La différence est sur la ligne avec swizzledMethod qui devrait être:

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

L'ensemble du code mis à jour est:

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)
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.