UISplitViewController 在键盘出现时调整应用程序内容 iPhone

UISplitViewController Resizing App Content when Keyboard Appears on iPhone

我的视图嵌套在 SplitViewController 中,SplitViewController.preferedDisplayMode.primaryOverlay

当用户在水平紧凑的设备上调出键盘时,我的应用程序的整个内容视图将调整大小以仅包括键盘上方的区域和任何输入附件视图。

这与键盘出现在内容上的预期行为相反(将内容嵌入应用于滚动视图,以便用户可以看到重要的内容)。

通过旋转设备,这种调整大小的行为就会消失。事实上,单次旋转后,该行为不会再次发生,直到应用程序重新启动(即使您旋转回原来的旋转)。

调试快照

这些照片是在 iPhone 上拍摄的,为清晰起见,拆分视图已折叠。

这是旋转或键盘前内容的视图调试快照:

这是在键盘出现后调整大小的内容的调试快照(调整大小的更后视图是我没有创建的 UIPopoverView)。 请注意,键盘不在此快照中,因为 iOS 将键盘放在 RemoteKeyboardWindow 中。

这里是调试快照和查看内容的调试快照,旋转为横向然后返回纵向,并使用键盘(问题已消失):

任何文本输入视图都会出现此问题。

这对我来说是一个奇怪的问题,所以如果有人以前见过它或知道要采取的任何步骤,我们将不胜感激。

我通过 this wonderful blog post about existing bugs in UISplitViewController 找到了这个问题的原因。

原因

当水平紧凑环境请求 .primaryOverlay 样式时,会出现该错误。该样式将覆盖层包裹在弹出窗口中,并且所有弹出窗口都会自动调整大小以避免键盘。因为水平紧凑的设备只显示一个视图,所以这将所有应用程序内容包裹在一个调整大小的弹出窗口中。

原来 UISplitViewController 有一个未记录的行为,在该行为中旋转设备会导致 preferedDisplayMode 属性 重新计算。在水平紧凑设备的情况下,重新评估会将 属性 设置回 .automatic(或 .primaryHidden),这将停止将内容包装在调整大小的弹出窗口中,从而解决问题。

回答

解决方案是根据设备特性手动设置 preferedDisplayMode 属性,无论是在初始化时还是特性更改时。

我能够通过如下子类化 UISplitViewController 来解决问题,同时保持 iPad 上的当前行为:

// Swift 3
class AdaptiveSplitViewController : UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        evaulateOverlayType(for: self.traitCollection)
    }

    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        super.willTransition(to: newCollection, with: coordinator)

        evaulateOverlayType(for: newCollection)
    }

    private func evaulateOverlayType(for traits: UITraitCollection) {
        if traits.horizontalSizeClass == .regular {
            self.preferredDisplayMode = .primaryOverlay
        } else {
            self.preferredDisplayMode = .automatic
        }
    }

}