返回

iPad Sheet 尺寸异常? preferredContentSize 失效解决方案

IOS

iPad上Sheet无法尊重preferredContentSize问题解析

在iOS应用开发中,SheetPresentationController 为开发者提供了一种方便的方式,以模态形式呈现视图控制器。在紧凑尺寸(如 iPhone)下,Sheet 效果是自下向上滑出的。而对于大尺寸屏幕设备(如 iPad),我们期望视图控制器以“表单”形式(即浮动居中的窗口)展示。一种常见的需求,视图控制器的内容高度会根据 preferredContentSize 自行调整。但是,可能会遇到这样的问题,在 iPad 上 Sheet 并未按照预期尊重视图控制器的 preferredContentSize ,占据几乎整个屏幕,而非我们期望的较小尺寸窗口。本文探讨该问题及其解决方案。

问题分析

根本原因往往是 SheetPresentationController 默认行为和大屏设备窗口大小之间的不匹配。当系统决定如何布局和呈现一个模态视图时,它会考量诸如目标窗口的大小,可用的屏幕空间,以及视图控制器自身的尺寸等因素。当 preferredContentSize 不起作用时,可能是由于系统在计算 Sheet 大小时覆盖了这一属性。换言之,系统可能根据一些内部布局策略(例如自动适应可用空间)而非preferredContentSize 进行视图的调整。这在大尺寸的 iPad 屏幕上更易发生,因为可用空间充足,导致系统“倾向”于占用更多空间,并且 preferredContentSize 属性的计算和传递可能存在一定的问题。

解决方案一:调整 PresentationController 的 preferredContentSize

一种方法是不仅在呈现的视图控制器上设置 preferredContentSize,还在负责呈现该视图的 UIPresentationControllerSheetPresentationController 本身就是一个 UIPresentationController)上设置。这需要自定义 Presentation Controller,并在该控制器中指定所需的大小,这样可以覆盖系统默认的行为。

代码示例:

class CustomSheetPresentationController: UISheetPresentationController {

    override var preferredContentSize: CGSize {
        get { presentedViewController.preferredContentSize }
        set {
            presentedViewController.preferredContentSize = newValue
            super.preferredContentSize = newValue
        }
    }

    override func presentationTransitionWillBegin() {
         super.presentationTransitionWillBegin()
         containerView?.backgroundColor = UIColor.black.withAlphaComponent(0.3)
    }
}

extension UIViewController {

    func presentSheet(viewController: UIViewController, preferredSize: CGSize) {
        viewController.preferredContentSize = preferredSize
        viewController.modalPresentationStyle = .custom
        let presentationController = CustomSheetPresentationController(presentedViewController: viewController, presenting: self)
        presentationController.detents = [.medium()]
         viewController.transitioningDelegate = presentationController

        present(viewController, animated: true)
    }
}

操作步骤:

  1. 创建一个自定义的 CustomSheetPresentationController ,该控制器继承自 UISheetPresentationController
  2. 重写 preferredContentSize 属性,将值同步传递给 presentedViewController
  3. 重写 presentationTransitionWillBegin 函数。该函数是在模态视图控制器即将呈现之前调用的。通过该函数设置模态视图的背景颜色,使得背景具有透明效果,以改善用户的体验。
  4. 编写 UIViewController 扩展方法 presentSheet 用于代替默认的 present 操作,创建自定义的CustomSheetPresentationController实例,然后使用presentationController.detents 添加必要的模态滑入停靠点。调用present 呈现视图控制器。

该方案利用自定义 PresentationController 接管展示流程,并且准确设置所需大小。 detents 设置是避免sheet 占据整个屏幕的关键一步。

解决方案二:使用UIModalPresentationStyle.formSheet

另一种直接的方式是明确设置 modalPresentationStyle.formSheet。这个属性直接指定模态视图应当以表单形式呈现。这应该能迫使 UIKit 使用一个符合 preferredContentSize 的固定尺寸,而不再尝试根据设备窗口大小自动调整。此方法无需自定义 Presentation Controller,较为简单快捷。

代码示例:

let viewController = YourViewController() // Replace with actual view controller
viewController.preferredContentSize = CGSize(width: 300, height: 400) // set your prefered size
viewController.modalPresentationStyle = .formSheet

present(viewController, animated: true, completion: nil)

操作步骤:

  1. 创建你要呈现的视图控制器实例。
  2. 设置其 preferredContentSize 为所需的大小。
  3. 将其 modalPresentationStyle 属性设置为 .formSheet
  4. 使用 present(_:animated:completion:) 方法呈现视图控制器。

使用这种方法可以使 Sheet 在 iPad 上以较小的固定大小显示,强制系统使用你指定的 preferredContentSize,它避免了因为窗口过大而影响视觉效果的问题。此方式相比方法一更加简单,但失去了对于 Sheet 效果更细粒度的控制,无法进行滑动停靠等交互。

安全建议

使用这些方案,仍有一些注意事项需要遵守:

  • 内容自适应 :确保内部布局适配各种尺寸的 preferredContentSize,不要假设一个固定的尺寸。
  • 屏幕旋转 : 处理设备旋转的情况,调整 preferredContentSize 或 使用 Size Class 逻辑 来更新显示,保证布局的正确性。
  • 最小尺寸 :即使使用 preferredContentSize,也要考虑到模态视图内容的最小可接受尺寸,以避免用户界面无法正确显示的情况。

合理运用这些解决方案,可以保证 iOS 应用中 Sheet 视图在各种尺寸屏幕上(尤其是 iPad)呈现预期效果,实现优秀用户体验。