Лист действий с наложением клавиатуры в iOS 13.1 на CNContactViewController

Кажется, это характерно для iOS 13.1, так как в iOS 13.0 и более ранних версиях он работает, как ожидалось, для добавления контакта в CNContactViewController, если я "Отмена", лист действий перекрывается клавиатурой. Никаких действий не выполняется, и клавиатура не отключается.

7 ответов

Престижность @GxocT за отличный обходной путь! Очень помог моим пользователям.
Но я хотел поделиться своим кодом, основанным на решении @GxocT, в надежде, что оно поможет другим в этом сценарии.

Мне нужен был мой CNContactViewControllerDelegate contactViewController(_:didCompleteWith:) быть вызванным при отмене (как и сделано).

Также моего кода не было в UIViewController так что нет self.navigationController

Я также не люблю использовать принудительное разворачивание, когда могу с этим помочь. Меня кусали в прошлом, поэтому я приковалif lets в настройке

Вот что я сделал:

  1. Расширить CNContactViewControllerи поместите туда функцию swizzle
    .

  2. В моем случае в функции swizzle просто вызовите
    CNContactViewControllerDelegate делегировать
    contactViewController(_:didCompleteWith:) с self а также
    self.contact объект из контроллера контактов

  3. В установочном коде убедитесь, что вызов swizzleMethodclass_getInstanceMethod определяет CNContactViewControllerкласс вместо self

И код 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)
    }
}

Клавиатура по-прежнему отображается на мгновение, но отключается сразу после закрытия контроллера контактов.
Будем надеяться, что яблоко исправит это

Я не мог найти способ закрыть клавиатуру. Но, по крайней мере, вы можете открыть ViewController, используя мой метод.

  1. Не знаю почему, но отключить клавиатуру в CNContactViewController невозможно. Я пробовал endEditing:, создать новый UITextField firstResponder и так далее. Ничего не получилось.
  2. Я попытался изменить действие кнопки "Отменить". Вы можете найти эту кнопку в стеке NavigationController, но ее действие меняется каждый раз, когда вы что-то вводите.
  3. Наконец я использовал метод swizzling. Я не смог найти способ закрыть клавиатуру, как упоминал ранее, но, по крайней мере, вы можете закрыть 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/

Исправлено в iOS 13.4. Проверено в Xcode Simulator.

Фактически, пользователь может смахнуть вниз, чтобы закрыть клавиатуру, а затем нажать "Отмена" и просмотреть лист действий. Так что эта проблема достойна сожаления и определенно является ошибкой (и я подал отчет об ошибке), но не фатальной (хотя, конечно, обходной путь для пользователя не является тривиальным).

Спасибо, @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)
    }
}

Спасибо GxocT за отличную работу. Я думаю, что это очень полезный вопрос и пост для тех, кто работает сCNContactViewController. У меня тоже была эта проблема (до сих пор), но в объекте c. Я интерпретирую приведенный выше код Swift как цель 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 вы можете попробовать этот пост Мэтта

Всегда следует учитывать, что метод swizzler выполняется только один раз. Убедитесь, что вы реализовали changeCancelImplementation() в очереди dispatch_once, чтобы он выполнялся только один раз.

Проверьте эту ссылку для описания

Также эта ошибка встречается только в iOS 13.1, 13.2 и 13.3.

Другие вопросы по тегам