Aktionsblatt zur Tastaturüberlagerung in iOS 13.1 auf CNContactViewController

12

Dies scheint spezifisch für iOS 13.1 zu sein, da es unter iOS 13.0 und früheren Versionen wie erwartet funktioniert, um einen Kontakt in CNContactViewController hinzuzufügen. Wenn ich auf "Abbrechen" klicke, überlappt sich das Aktionsblatt mit der Tastatur. Es werden keine Aktionen ausgeführt und die Tastatur wird nicht geschlossen.

mounika 582
quelle

Antworten:

5

Ein großes Lob an @GxocT für die großartige Problemumgehung! Hat meinen Nutzern sehr geholfen.
Aber ich wollte meinen Code basierend auf der @ GxocT-Lösung teilen, in der Hoffnung, dass er anderen in diesem Szenario hilft.

Ich musste CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)beim Abbrechen angerufen werden (und auch erledigt).

Auch mein Code war nicht in einem UIViewControllerso gibt es keinself.navigationController

Ich mag es auch nicht, Gewalt auszuwickeln, wenn ich helfen kann. Ich wurde in der Vergangenheit gebissen, also habe ich if lets im Setup angekettet

Folgendes habe ich getan:

  1. Erweitern CNContactViewControllerund platzieren Sie die Swizzle-Funktion
    dort.

  2. In meinem Fall in der Swizzle-Funktion rufen Sie einfach den
    CNContactViewControllerDelegateDelegaten
    contactViewController(_:didCompleteWith:)mit selfund
    self.contactObjekt vom Kontakt-Controller auf

  3. Stellen Sie im Setup-Code sicher, dass der Aufruf von swizzleMethod class_getInstanceMethoddie CNContactViewController Klasse anstelle von angibtself

Und der Swift-Code:

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

Die Tastatur wird weiterhin kurz angezeigt, fällt jedoch unmittelbar nach dem Schließen des Kontakt-Controllers ab.
Hoffen wir, dass Apple das behebt

Barrett
quelle
Force Unwraps werden verwendet, um Code zu kompaktieren. Natürlich sollten Sie sie nach Möglichkeit vermeiden und wenn dies zum Absturz führen kann. Übrigens bin ich mir nicht sicher, ob es richtig ist, self.contact an delegate zu übergeben, da es wahrscheinlich nicht erstellt wird, wenn Sie flow ps abbrechen: Meine Implementierung wurde geändert, um Force-Unwraps zu vermeiden: D
GxocT
@GxocT - vereinbart auf die Kraft urwraps. Und sie sind nicht immer schrecklich, aber andere sind nicht wie Sie und können sie immer benutzen, ohne das Risiko zu erkennen;). Ich mag wenn statt lassen! wenn ich nicht 100% sicher bin, wird es nicht Null sein. Über den self.contact - es ist wahr Ich weiß nicht, was der lib-Code von Apple normalerweise intern an den Delegaten weitergibt, aber self.contact hatte die Kontaktdaten und das CNContactViewController-Dokument sagt, es sei "der angezeigte Kontakt", daher schien es in Ordnung zu sein, ihn zu verwenden. Mein Code verwendet nicht den Kontakt, der im Abschlussdelegierten übergeben wurde, sodass ich in der Erweiterung einfach Null übergeben konnte.
Barrett
Der Inhalt (einschließlich Textansichten und Tastaturen) in CNContactViewController sollte sich in einem separaten Prozess befinden. Wenn Sie in Xcode für diesen Ansichtscontroller die Ansichtshierarchie verwenden können, wird der Inhalt möglicherweise nicht angezeigt. Daher können wir weder die Tastatur noch die Textansicht steuern.
WildCat
5

Ich konnte keinen Weg finden, die Tastatur zu schließen. Aber zumindest können Sie ViewController mit meiner Methode öffnen.

  1. Ich weiß nicht warum, aber es ist unmöglich, die Tastatur in CNContactViewController zu schließen. Ich habe versucht, endEditing:, neue UITextField firstResponder und so weiter zu machen. Nichts hat geklappt.
  2. Ich habe versucht, die Aktion für die Schaltfläche "Abbrechen" zu ändern. Sie finden diese Schaltfläche im NavigationController-Stapel. Die Aktion wird jedoch jedes Mal geändert, wenn Sie etwas eingeben.
  3. Schließlich habe ich Methode Swizzling verwendet. Ich konnte keine Möglichkeit finden, die Tastatur zu schließen, wie ich bereits erwähnt habe, aber zumindest können Sie CNContactViewController schließen, wenn die Schaltfläche "Abbrechen" gedrückt wird.
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: Weitere Informationen zum Thema reddit finden Sie unter: https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/

GxocT
quelle
2

Der Benutzer kann tatsächlich nach unten wischen, um die Tastatur zu schließen, und dann auf Abbrechen tippen, um das Aktionsblatt anzuzeigen. Daher ist dieses Problem bedauerlich und definitiv ein Fehler (und ich habe einen Fehlerbericht eingereicht), aber nicht schwerwiegend (obwohl die Problemumgehung für den Benutzer nicht trivial ist).

Geben Sie hier die Bildbeschreibung ein

matt
quelle
Könnten Sie einen Link zu dem von Ihnen eingereichten Fehlerbericht erstellen?
Paaske
1

In iOS 13.4 behoben. In Xcode Simulator getestet

Peretz30
quelle
1

Vielen Dank an @Gxoct für seine hervorragende Arbeit. Ich denke, dies ist eine sehr nützliche Frage und Post für diejenigen, die mit arbeiten CNContactViewController. Ich hatte auch dieses Problem (bis jetzt), aber in Ziel c. Ich interpretiere den obigen Swift-Code in Ziel 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);
    }
}

Erstellen einer CNContactViewControllerKategorie für den Zugriff auf Entlassung;

@implementation CNContactViewController (Test)

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

@end

Leute, die Swizzling nicht so gut kennen, können diesen Beitrag von matt ausprobieren

Sk Borhan Uddin
quelle
0

Vielen Dank, @GxocT, für Ihre Problemumgehung. Die hier veröffentlichte Lösung unterscheidet sich jedoch von der auf Reddit veröffentlichten.

Der auf Reddit funktioniert für mich, dieser nicht, also möchte ich ihn hier erneut veröffentlichen. Der Unterschied liegt in der Zeile mit swizzledMethod, die sein sollte:

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

Der gesamte aktualisierte Code lautet:

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
quelle