点击文本字段,解除键盘并再次点击文本字段时,UIWebView崩溃

时间:2017-03-22 14:51:37

标签: ios cocoa-touch uiwebview

我的应用支持来自多个云服务(Dropbox,Google Drive等)的授权,并使用适当的SDK。每个SDK都通过呈现Web视图来提供授权,用户可以在其中输入其凭据。问题是,在以下情况下,Web视图会使应用程序崩溃:

  1. 展示网络视图
  2. 点按任何文字字段
  3. 出现键盘,按“完成”按钮
  4. 点按您在步骤2中点按的文字字段
  5. 崩溃
  6. 以下信息将打印到控制台中。

    2017-03-22 19:17:55.564 MyApplicationName[46315:16017957] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
    *** First throw call stack:
    (
        0   CoreFoundation                      0x000000010e63ff65 __exceptionPreprocess + 165
        1   libobjc.A.dylib                     0x0000000111960deb objc_exception_throw + 48
        2   CoreFoundation                      0x000000010e506f55 -[__NSArrayM insertObject:atIndex:] + 901
        3   UIKit                               0x000000011206fcd4 -[UIViewController _addChildViewController:performHierarchyCheck:notifyWillMove:] + 541
        4   UIKit                               0x00000001127e9581 -[UIInputWindowController changeToInputViewSet:] + 1420
        5   UIKit                               0x00000001127e218f -[UIInputWindowController moveFromPlacement:toPlacement:starting:completion:] + 369
        6   UIKit                               0x00000001127ea01b -[UIInputWindowController setInputViewSet:] + 983
        7   UIKit                               0x00000001127e1e17 -[UIInputWindowController performOperations:withAnimationStyle:] + 50
        8   UIKit                               0x00000001124fb397 -[UIPeripheralHost(UIKitInternal) setInputViews:animationStyle:] + 1275
        9   UIKit                               0x00000001120e0c89 -[UIResponder(UIResponderInputViewAdditions) reloadInputViews] + 81
        10  UIKit                               0x0000000112527604 -[UIWebBrowserView _reloadInputViewsAfterPotentialFocusRedirect] + 54
        11  UIKit                               0x00000001125274a9 -[UIWebBrowserView _endAllowingFocusRedirects] + 45
        12  libdispatch.dylib                   0x00000001149d149b _dispatch_client_callout + 8
        13  libdispatch.dylib                   0x00000001149bacd8 _dispatch_barrier_sync_f_slow_invoke + 284
        14  libdispatch.dylib                   0x00000001149d149b _dispatch_client_callout + 8
        15  libdispatch.dylib                   0x00000001149b934b _dispatch_main_queue_callback_4CF + 1738
        16  CoreFoundation                      0x000000010e5a03e9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
        17  CoreFoundation                      0x000000010e561939 __CFRunLoopRun + 2073
        18  CoreFoundation                      0x000000010e560e98 CFRunLoopRunSpecific + 488
        19  GraphicsServices                    0x0000000115eafad2 GSEventRunModal + 161
        20  UIKit                               0x0000000111ee0676 UIApplicationMain + 171
        21  MyApplicationName                   0x000000010c161682 main + 114
        22  libdyld.dylib                       0x0000000114a0592d start + 1
        23  ???                                 0x0000000000000001 0x0 + 1
    )
    libc++abi.dylib: terminating with uncaught exception of type NSException
    

    显然,在点击文本字段时会出现此问题,因此我决定通过打开google.com并点击其中的文本字段来测试它,以确保应用程序崩溃。 该应用程序没有崩溃,但在我显示/隐藏键盘时它确实打印了以下信息。

    2017-03-22 20:31:52.453 MyApplicationName[55324:16092779] *** WebKit discarded an uncaught exception in the webView:elementDidFocusNode: delegate: <NSInvalidArgumentException> *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
    2017-03-22 20:31:54.985 MyApplicationName[55324:16092779] *** WebKit discarded an uncaught exception in the webView:elementDidBlurNode: delegate: <NSInvalidArgumentException> *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
    2017-03-22 20:31:56.341 MyApplicationName[55324:16092779] *** WebKit discarded an uncaught exception in the webView:elementDidFocusNode: delegate: <NSInvalidArgumentException> *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
    2017-03-22 20:31:58.029 MyApplicationName[55324:16092779] *** WebKit discarded an uncaught exception in the webView:elementDidBlurNode: delegate: <NSInvalidArgumentException> *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
    2017-03-22 20:32:00.675 MyApplicationName[55324:16092779] *** WebKit discarded an uncaught exception in the webView:elementDidFocusNode: delegate: <NSInvalidArgumentException> *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
    2017-03-22 20:32:02.872 MyApplicationName[55324:16092779] *** WebKit discarded an uncaught exception in the webView:elementDidBlurNode: delegate: <NSInvalidArgumentException> *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
    

    我怀疑键盘的工具栏存在一些问题,因为如果你看一下this gif,你会发现工具栏第一次出现,但第二次没有出现,出现了QuickType一会儿。

    其他信息: - 我无法在示例项目中重现该问题,即当我试图在google.com上崩溃应用程序时,日志中没有崩溃或错误。 - 我不太可能在UIWindow上有一些手势识别器,在那里检查。

    所以问题是:我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

在UIInputWindowController的changeToInputViewSet中,您尝试添加子视图控制器,但该子视图控制器为nil。找出你的视图控制器为零的原因,然后解决这个问题。你有一个可重复的序列来揭示崩溃。在changeToInputViewSet的开头放置一个断点,并逐步查看您正在使用的任何viewControllers的值。这是你的错误。

答案 1 :(得分:0)

好的,所以我设法找出导致这种行为的原因。下面的这个小扩展导致了这个错误。

extension UIViewController {
    /**
     Simple shortcut for trivial cases.
     Adds a child controller to the view, that is a descendant of `self.view`.
     By default, child controller's view is stretched in the passed view.
     Use `constraintsSetupBlock` to setup constraints as you need.
     */
    func addChildViewController(childController: UIViewController, to view: UIView, constraintsSetupBlock: ((view: UIView, childControllerView: UIView) -> Void)? = nil) {
        guard view.isDescendantOfView(self.view) else {
            fatalError("`view` must be a descendand of `self.view`")
        }

        addChildViewController(childController)
        view.addSubview(childController.view)
        if let setupBlock = constraintsSetupBlock {
            setupBlock(view: view, childControllerView: childController.view)
        } else {
            childController.view.snp_makeConstraints(closure: { (make) in
                make.edges.equalTo(view)
            })
        }
        childController.didMoveToParentViewController(self)
    }

    /**
     Simple shortcut for trivial cases.
     `childController` is optional purely for convenience.
    */
    func removeChildViewController(childController: UIViewController?) {
        guard let controller = childController else { return }
        controller.willMoveToParentViewController(nil)
        controller.view.removeFromSuperview()
        controller.didMoveToParentViewController(nil)
    }
}

我的猜测是这些方法的名称与某些SDK的私有方法相匹配,并且在运行时系统选择了我的实现,这导致了非常不愉快的结果。