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.

mounika 582
źródło

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

Barrett
źródło
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/

GxocT
źródło
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

matowy
źródło
Czy możesz podać link do zgłoszonego raportu o błędzie?
Paaske,
1

Naprawiono w iOS 13.4 Testowane w Xcode Simulator

Peretz30
źródło
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

Sk Borhan Uddin
źródło
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)
    }
}
jakub1984
źródło