Клавіатурний лист дії в iOS 13.1 на CNContactViewController


12

Це здається специфічним для iOS 13.1, оскільки він працює, як очікувалося, на iOS 13.0 та більш ранніх версіях, щоб додати контакт у CNContactViewController, якщо я "Скасувати", аркуш дій перекривається клавіатурою. Жодні дії, які виконуються, а клавіатура не відхиляє.

Відповіді:


5

Кудо до @GxocT за чудове рішення! Дуже допомагав моїм користувачам.
Але я хотів поділитися своїм кодом на основі рішення @GxocT, сподіваючись, що він допоможе іншим у цьому сценарії.

Мені потрібно було, CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)щоб мене викликали при скасуванні (як і зроблено).

Також мого коду не було, UIViewControllerтак що немаєself.navigationController

Мені також не подобається використовувати силу розгортання, коли я можу допомогти. Мене покусали в минулому, тому я прикував if letдо себе налаштування

Ось що я зробив:

  1. Розкладіть CNContactViewControllerі помістіть туди функцію swizzle
    .

  2. У моєму випадку у функції Swizzle просто викличте
    CNContactViewControllerDelegateделегат
    contactViewController(_:didCompleteWith:)з selfі
    self.contactоб'єкт з контакту контролера

  3. У коді установки переконайтеся, що виклик swizzleMethod class_getInstanceMethodвказує CNContactViewController клас замістьself

І код Свіфта:

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

Клавіатура все ще на мить відображається, але опускається відразу після відключення контролера "Контакти".
Сподіваємось, яблуко це виправить


Сила розгортання використовується, щоб зробити код компактним, звичайно, ви повинні уникати їх, якщо це можливо, і якщо це може призвести до збоїв. btw Я не впевнений, чи правильно передавати self.contact для делегування, тому що він, ймовірно, не створений, якщо ви скасуєте потік ps: змінив мою реалізацію, щоб уникнути силових розгортань: D
GxocT

@GxocT - домовились про силові перемички. І вони не завжди страшні, але інші не схожі на вас і можуть завжди використовувати їх, не усвідомлюючи ризику;). Мені подобається, якщо дозволяють замість! коли я не на 100% впевнений, що він не буде нульовим. Про self.contact - його правда, я не знаю, який код ліб Apple зазвичай передається внутрішньому делегату, але self.contact мав контактні дані, і доктор CNContactViewController каже, що це "контакт, який відображається", тому використовувати його здавалося нормально. Мій код насправді не використовує контакт, переданий у делегаті завершення, щоб я міг просто передати нуль у розширенні.
Барретт

Вміст (включаючи перегляди тексту та клавіатури) у CNContactViewController повинен бути окремим процесом. Якщо ви можете використовувати "Перегляд ієрархії" в Xcode для цього контролера перегляду, ви можете знайти вміст, який не можна побачити. Як результат, ми не можемо керувати переглядом клавіатури та тексту.
WildCat

5

Не вдалося знайти спосіб відхилити клавіатуру. Але принаймні ви можете висказати ViewController, використовуючи мій метод.

  1. Не знаю, чому, але неможливо відхилити клавіатуру в CNContactViewController. Я спробував endEditing:, створити новий UITextField firstResponder тощо. Нічого не працювало.
  2. Я спробував змінити дію для кнопки "Скасувати". Цю кнопку ви можете знайти в стеку NavigationController, але її дія змінюється кожного разу, коли ви щось вводите.
  3. Нарешті я застосував метод свистування. Не вдалося знайти спосіб відхилення клавіатури, як я вже згадував раніше, але принаймні ви можете відмовитись від CNContactViewController, якщо натиснути кнопку "Скасувати".
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: Ви можете знайти додаткову інформацію по темі reddit: https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/


2

Користувач може насправді провести пальцем вниз, щоб відхилити клавіатуру, а потім натисніть Скасувати і переглянути список дій. Тож ця проблема викликає жаль і, безумовно, помилка (і я подав звіт про помилку), але не є фатальною (хоча, напевне, вирішення проблеми не є банальним для користувача, щоб виявити).

введіть тут опис зображення


Чи можете ви зв’язатись із поданим звітом про помилку?
ПОСК


1

Дякуємо @Gxoct за його відмінну роботу. Я думаю, що це дуже корисне питання та повідомлення для тих, хто працює CNContactViewController. У мене також була ця проблема (дотепер), але в об'єктивній c. Я інтерпретую вищезгаданий код Свіфта в ціль 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);
    }
}

Створення CNContactViewControllerкатегорії для доступу до звільнення;

@implementation CNContactViewController (Test)

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

@end

Хлопці, які не так добре знайомі з Swizzling, ви можете спробувати цей пост за допомогою matt


0

Дякую, @GxocT за ваше вирішення, однак рішення, розміщене тут, відрізняється від рішення, яке ви розмістили на Reddit.

Той, що працює на Reddit, працює для мене, цей не є, тому я хочу його переробити тут. Різниця полягає в рядку з swizzledMethod, який повинен бути:

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

Весь оновлений код:

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)
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.