CNContactViewController上的iOS 13.1中的键盘覆盖操作表


12

这似乎是特定于iOS 13.1的,因为它可以在iOS 13.0和更早版本上按预期工作,可以在CNContactViewController中添加联系人,如果我单击“取消”,操作表会被键盘重叠。没有执行任何操作,也没有关闭键盘。

Answers:


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

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

键盘仍会暂时显示,但在Contacts控制器关闭后会立即落下。
希望苹果能解决这个问题


强制拆包用于使代码紧凑,当然,如果可能并可能导致崩溃,则应避免使用它们。顺便说一句,我不确定是否将self.contact传递给代理,是否正确,因为如果您取消流程ps,可能就不会创建它:更改了我的实现,以避免强行打开包装:D
GxocT

@GxocT-同意使用urwraps。而且它们并不总是那么可怕,但是其他人却不像您,并且可能总是在没有意识到风险的情况下使用它们;)。我喜欢,如果让而不是!当我不是100%肯定不会为零时。关于self.contact-确实如此,我不知道苹果的lib代码通常会在内部传递给该委托,但是self.contact具有联系人数据,CNContactViewController文档说它是“正在显示的联系人”,因此似乎可以使用。我的代码实际上并没有使用在完成委托中传递的联系人,因此我可以在扩展名中传递nil。
巴雷特

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

实际上,用户可以向下滑动以关闭键盘,然后点击“取消”以查看操作表。因此,此问题是令人遗憾的,并且绝对是一个错误(并且我已经提交了一个错误报告),但不是致命的(尽管,可以肯定的是,对于用户来说,变通办法并不容易)。

在此处输入图片说明


您能否链接到您提交的错误报告?
Paaske


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的人,您可以尝试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)
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.