Nakładka klawiatury nakładająca się na arkusz akcji w iOS 13.1 na CNContactViewController


12

Wydaje się to być specyficzne dla iOS 13.1, ponieważ działa zgodnie z oczekiwaniami w iOS 13.0 i wcześniejszych wersjach, aby dodać kontakt w CNContactViewController, jeśli „anuluję”, arkusz akcji nakłada się na klawiaturę. Żadne czynności nie są wykonywane, a klawiatura nie zwalnia.

Odpowiedzi:


5

Wyrazy uznania dla @GxocT za wspaniałe obejście! Ogromnie pomogło moim użytkownikom.
Chciałem jednak udostępnić mój kod oparty na rozwiązaniu @GxocT, mając nadzieję, że pomoże to innym osobom w tym scenariuszu.

Musiałem CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)zostać wezwany do anulowania (a także gotowe).

Również mój kod nie był w, UIViewControllerwięc nie maself.navigationController

Nie lubię też wymuszać rozpakowywania, gdy mogę pomóc. Byłem gryziony w przeszłości, więc przykuty łańcuchem if letw konfiguracji

Oto co zrobiłem:

  1. Rozwiń CNContactViewControlleri umieść tam funkcję swizzle
    .

  2. W moim przypadku w funkcji swizzle tylko wywołać
    CNContactViewControllerDelegatedelegata
    contactViewController(_:didCompleteWith:)z selforaz
    self.contactobiektu z kontrolera kontaktowego

  3. W kodzie instalacyjnym upewnij się, że wywołanie swizzleMethod class_getInstanceMethodokreśla CNContactViewController klasę zamiastself

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

Klawiatura nadal wyświetla się przez chwilę, ale spada po tym, jak kontroler Kontakty zwalnia.
Miejmy nadzieję, że jabłko to naprawi


wymuszanie rozpakowywania jest używane, aby kod był zwarty, oczywiście należy go unikać, jeśli to możliwe i jeśli może to doprowadzić do awarii. btw nie jestem pewien, czy jest to poprawne przekazać self.contact do delegata, bo to chyba nie jest tworzony jeśli odwołać ps przepływu: zmieniłem wdrożenie do rozpakowuje sił unikać: D
GxocT

@GxocT - uzgodniono zapisywanie sił. I nie zawsze są straszne, ale inni nie są tacy jak ty i zawsze mogą ich używać, nie zdając sobie sprawy z ryzyka;). Lubię, jeśli pozwolę zamiast! kiedy nie jestem w 100% pewien, że to nie będzie zero. O self.contact - to prawda Nie wiem, jaki kod lib Apple zwykle przekazuje delegatowi wewnętrznie, ale self.contact miał dane kontaktowe, a dokument CNContactViewController mówi, że „kontakt jest wyświetlany”, więc korzystanie z niego wydawało się w porządku. Mój kod faktycznie nie używa kontaktu przekazanego w delegacie zakończenia, więc mógłbym po prostu przekazać zero w rozszerzeniu.
Barrett,

Zawartość (w tym widoki tekstu i klawiatury) w CNContactViewController powinna być w osobnym procesie. Jeśli możesz użyć „Wyświetl hierarchię” w Xcode dla tego kontrolera widoku, może się okazać, że zawartość nie jest widoczna. W rezultacie nie możemy kontrolować klawiatury ani widoku tekstu.
WildCat

5

Nie mogłem znaleźć sposobu na wyłączenie klawiatury. Ale przynajmniej możesz pop ViewController za pomocą mojej metody.

  1. Nie wiem dlaczego, ale nie można zamknąć klawiatury w CNContactViewController. Próbowałem endEditing :, uczyń nowy UITextField firstResponder i tak dalej. Nic nie działało.
  2. Próbowałem zmienić działanie przycisku „Anuluj”. Możesz znaleźć ten przycisk w stosie Nawigatora Sterowania, ale jego działanie jest zmieniane za każdym razem, gdy coś wpisujesz.
  3. Wreszcie użyłem metody swizzling. Nie mogłem znaleźć sposobu na zwolnienie klawiatury, jak wspomniałem wcześniej, ale przynajmniej możesz zamknąć CNContactViewController po naciśnięciu przycisku „Anuluj”.
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: Możesz znaleźć dodatkowe informacje na temat reddit: https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/


2

Użytkownik może w rzeczywistości przesunąć palcem w dół, aby zamknąć klawiaturę, a następnie stuknij Anuluj i zobacz arkusz akcji. Więc ten problem jest godny ubolewania i na pewno jest błędem (a ja zgłosiłem raport o błędzie), ale nie jest fatalny (choć, dla pewności, obejście nie jest trywialne dla użytkownika).

wprowadź opis zdjęcia tutaj


Czy możesz podać link do zgłoszonego raportu o błędzie?
Paaske,


1

Dzięki @Gxoct za jego doskonałą pracę. Myślę, że jest to bardzo przydatne pytanie i post dla tych, którzy pracują CNContactViewController. Miałem też ten problem (do tej pory), ale w celu c. Interpretuję powyższy kod Swift na cel 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);
    }
}

Tworzenie CNContactViewControllerkategorii dostępu do zwolnienia;

@implementation CNContactViewController (Test)

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

@end

Faceci, którzy nie są tak obeznani ze Swizzlingiem, mogą spróbować tego postu przez mat


0

Dziękujemy, @GxocT za obejście tego rozwiązania, jednak opublikowane tutaj rozwiązanie różni się od rozwiązania opublikowanego na Reddit.

Ten na Reddit działa dla mnie, ten nie, więc chcę go tutaj ponownie opublikować. Różnica dotyczy linii swizzledMethod, która powinna być:

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

Cały zaktualizowany kod to:

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)
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.