【发布时间】:2019-10-01 06:03:55
【问题描述】:
这似乎是 iOS 13.1 特有的,因为它在 iOS 13.0 及更早版本上按预期工作以在 CNContactViewController 中添加联系人,如果我“取消”,则操作表与键盘重叠。没有执行任何操作,键盘也没有关闭。
【问题讨论】:
标签: swift ios13 cncontact cncontactstore cncontactviewcontroller
这似乎是 iOS 13.1 特有的,因为它在 iOS 13.0 及更早版本上按预期工作以在 CNContactViewController 中添加联系人,如果我“取消”,则操作表与键盘重叠。没有执行任何操作,键盘也没有关闭。
【问题讨论】:
标签: swift ios13 cncontact cncontactstore cncontactviewcontroller
感谢@GxocT 的出色解决方法!极大地帮助了我的用户。
但我想基于@GxocT 解决方案分享我的代码,希望它能在这种情况下帮助其他人。
我需要我的 CNContactViewControllerDelegate contactViewController(_:didCompleteWith:) 在取消(以及完成)时被调用。
另外我的代码不在UIViewController 中,所以没有self.navigationController
我也不喜欢在可以提供帮助时使用强制解包。我过去被咬过,所以我在设置中链接了if lets
这是我所做的:
扩展 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。
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 模拟器中测试
【讨论】:
感谢@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 类别以访问dismiss;
@implementation CNContactViewController (Test)
- (void) dismiss{
[self.delegate contactViewController:self didCompleteWithContact:self.contact];
}
@end
对 Swizzling 不太熟悉的小伙伴可以试试这个 post by matt
【讨论】:
要始终考虑的一件事是 swizzler 方法只执行一次。确保在 dispatch_once 队列中实现 changeCancelImplementation() 以便它只执行一次。
Check this link for description
此外,此错误仅在 iOS 13.1、13.2 和 13.3 中发现
【讨论】: