【问题标题】:Keyboard overlaying action sheet in iOS 13.1 on CNContactViewController在 CNContactViewController 上的 iOS 13.1 中的键盘覆盖操作表
【发布时间】:2019-10-01 06:03:55
【问题描述】:

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

【问题讨论】:

    标签: swift ios13 cncontact cncontactstore cncontactviewcontroller


    【解决方案1】:

    感谢@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)
        }
    }
    

    键盘仍会暂时显示,但会在联系人控制器关闭后立即下降。
    让我们希望苹果能解决这个问题

    【讨论】:

    • force unwraps 用于使代码紧凑,当然,如果可能并且如果它可能导致崩溃,您应该避免它们。顺便说一句,我不确定将 self.contact 传递给委托是否正确,因为如果您取消流程,它可能不会创建 ps:更改了我的实现以避免强制解包:D
    • @GxocT - 同意强制 urwraps。而且它们并不总是很糟糕,但其他人不像你,可能总是在没有意识到风险的情况下使用它们;)。我喜欢 if let 而不是 !当我不是 100% 确定它不会为零时。关于 self.contact - 这是真的我不知道 Apple 的 lib 代码通常会在内部传递给委托,但 self.contact 有联系人数据,并且 CNContactViewController 文档说它是“正在显示的联系人”,所以它似乎可以使用。我的代码实际上并没有使用完成委托中传递的联系人,所以我可以在扩展中传递 nil。
    • CNContactViewController 中的内容(包括文本视图和键盘)应该在一个单独的进程中。如果您可以在 Xcode 中为此视图控制器使用“View Hierarchy”,您可能会发现看不到内容。结果,我们无法控制键盘和文本视图。
    【解决方案2】:

    我找不到关闭键盘的方法。但至少你可以使用我的方法弹出 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/

    【讨论】:

      【解决方案3】:

      在 iOS 13.4 中已修复 在 Xcode 模拟器中测试

      【讨论】:

        【解决方案4】:

        注意:此错误现已修复。此问题和答案仅适用于某些特定版本的 iOS(有限范围的 iOS 13 版本)。


        实际上,用户可以向下滑动以关闭键盘,然后点击取消并查看操作表。所以这个问题令人遗憾,而且绝对是一个错误(我已经提交了错误报告)但不是致命的(尽管可以肯定的是,解决方法对于用户来说并不是微不足道的发现)。

        【讨论】:

        • 能否链接到您提交的错误报告?
        【解决方案5】:

        感谢@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)
            }
        }
        

        【讨论】:

          【解决方案6】:

          感谢@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

          【讨论】:

            【解决方案7】:

            要始终考虑的一件事是 swizzler 方法只执行一次。确保在 dispatch_once 队列中实现 changeCancelImplementation() 以便它只执行一次。

            Check this link for description

            此外,此错误仅在 iOS 13.1、13.2 和 13.3 中发现

            【讨论】:

              猜你喜欢
              • 2012-09-12
              • 2020-09-10
              • 1970-01-01
              • 2019-01-26
              • 1970-01-01
              • 2020-03-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多