Лист действий с наложением клавиатуры в iOS 13.1 на CNContactViewController
Кажется, это характерно для iOS 13.1, так как в iOS 13.0 и более ранних версиях он работает, как ожидалось, для добавления контакта в CNContactViewController, если я "Отмена", лист действий перекрывается клавиатурой. Никаких действий не выполняется, и клавиатура не отключается.
7 ответов
Престижность @GxocT за отличный обходной путь! Очень помог моим пользователям.
Но я хотел поделиться своим кодом, основанным на решении @GxocT, в надежде, что оно поможет другим в этом сценарии.
Мне нужен был мой CNContactViewControllerDelegate
contactViewController(_:didCompleteWith:)
быть вызванным при отмене (как и сделано).
Также моего кода не было в UIViewController
так что нет self.navigationController
Я также не люблю использовать принудительное разворачивание, когда могу с этим помочь. Меня кусали в прошлом, поэтому я приковалif let
s в настройке
Вот что я сделал:
Расширить
CNContactViewController
и поместите туда функцию swizzle
.В моем случае в функции swizzle просто вызовите
CNContactViewControllerDelegate
делегироватьcontactViewController(_:didCompleteWith:)
сself
а такжеself.contact
объект из контроллера контактовВ установочном коде убедитесь, что вызов 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)
}
}
Клавиатура по-прежнему отображается на мгновение, но отключается сразу после закрытия контроллера контактов.
Будем надеяться, что яблоко исправит это
Я не мог найти способ закрыть клавиатуру. Но, по крайней мере, вы можете открыть ViewController, используя мой метод.
- Не знаю почему, но отключить клавиатуру в CNContactViewController невозможно. Я пробовал endEditing:, создать новый UITextField firstResponder и так далее. Ничего не получилось.
- Я попытался изменить действие кнопки "Отменить". Вы можете найти эту кнопку в стеке NavigationController, но ее действие меняется каждый раз, когда вы что-то вводите.
- Наконец я использовал метод 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/
Фактически, пользователь может смахнуть вниз, чтобы закрыть клавиатуру, а затем нажать "Отмена" и просмотреть лист действий. Так что эта проблема достойна сожаления и определенно является ошибкой (и я подал отчет об ошибке), но не фатальной (хотя, конечно, обходной путь для пользователя не является тривиальным).
Спасибо, @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.