Таблица действий наложения клавиатуры в iOS 13.1 на CNContactViewController


12

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

Ответы:


5

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

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

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

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

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

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

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

  3. В коде установки убедитесь, что вызов swizzleMethod class_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)
    }
}

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


Принудительное развертывание используется, чтобы сделать код компактным, конечно, вы должны избегать их, если это возможно, и если это может привести к сбою. Кстати, я не уверен, правильно ли передавать self.contact делегату, потому что он, вероятно, не создан, если вы отменяете поток ps: изменил мою реализацию, чтобы избежать принудительного развертывания: D
GxocT

@GxocT - согласился на насильственные действия. И они не всегда ужасны, но другие не такие, как вы, и могут всегда использовать их, не осознавая риска;). Мне нравится, если пусть вместо! когда я не на 100% уверен, что это не будет ноль. Что касается self.contact - это правда. Я не знаю, какой код lib Apple обычно передает делегату внутренне, но self.contact имел контактные данные, а документ CNContactViewController говорит, что это «отображаемый контакт», поэтому его можно было использовать нормально. Мой код на самом деле не использует контакт, переданный в делегате завершения, поэтому я мог просто передать nil в расширении.
Баррет

Содержимое (включая текстовые представления и клавиатуры) в CNContactViewController должно находиться в отдельном процессе. Если вы можете использовать 'View Hierarchy' в Xcode для этого контроллера представления, вы можете обнаружить, что содержимое не видно. В результате мы не можем контролировать ни клавиатуру, ни текстовое представление.
WildCat

5

Я не мог найти способ отклонить клавиатуру. Но, по крайней мере, вы можете открыть 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/


2

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

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


Не могли бы вы дать ссылку на сообщение об ошибке, которое вы подали?
Пааске


1

Спасибо @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, вы можете попробовать этот пост по мат


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.