从 Size Class 到可用空间,horizontalSizeClass 还可靠吗?

从 WWDC 26 开始,iPhone 应用在 iPhone Mirroring 到 Mac 时,其窗口尺寸将支持自由调整。与此同时,iPhone-only 应用在 iPad 上运行时,也会进入可变尺寸环境。即便不升级真机系统,开发者仍可以通过 Xcode 27 的 Preview 或 Device Hub 中的 iOS 27 模拟环境提前体验这一变化。

不过,这次更新带来的影响远不止“iPhone 应用可以调整尺寸”这么简单。它正在改变许多开发者长期以来对布局系统的理解。一些过去常被用作布局依据的 trait,例如 horizontalSizeClass 已不再适合作为窗口宽度的主要判断依据。

那么,这种变化究竟是一次突然出现的转向,还是 Apple 多年布局下的必然演进?本文将对此进行探讨。

我的 iPhone 应用变丑了

最近,我正在重构自己的一个老项目,尝试用新的开发思路和 API 重新实现整个应用。

当我看到 WWDC 26 上展示的 iPhone 应用可调整尺寸能力后,第一时间便测试了自己应用在这一场景下的表现。考虑到应用本身已经为 iPhone、iPad 和 macOS 预留了适配逻辑,我原本以为,在 Device Hub 中拉伸窗口宽度后,现有的自适应布局足以应对这种变化。

但出乎意料的是,NavigationSplitView 并没有从单列切换到多列,Tab 相关组件也没有出现预期中的形态变化。

当我检查环境中的 horizontalSizeClass 后发现,无论窗口尺寸如何变化,它始终保持为 compact

不过,当应用以 pad idiom 运行在 iPad 上时,horizontalSizeClass 仍会在一定范围内随着窗口尺寸变化而调整。如果移除 iPad device family,让应用以 iPhone-only 兼容模式运行在 iPad 上,其行为又会变得与 Device Hub 中的 iPhone host 十分接近。

起初,我一度怀疑这是 Beta 1 中尚未修复的问题。直到观看了 WWDC 26 的 Session《Modernize your UIKit app》后,我才意识到这其实是 Apple 有意设计的结果。

这不是 Beta Bug:WWDC26 给出的新契约

Apple 在 Session 278《Modernize your UIKit app》中,明确将 iPhone 应用纳入动态尺寸环境:

  • iPhone Mirroring on Mac 中,iPhone 窗口可自由调整尺寸。
  • iPhone-only 应用在 iPad 上运行时,也会进入可变尺寸环境。
  • 应用应能够在运行时适配任意 scene size,而不是假设固定设备比例。
  • UIScreen.main 与 screen bounds 不再可靠,应使用 window scene 的 effective geometry,或者 view / superview 的实际可用尺寸。
  • userInterfaceIdiom 不再适合作为布局决策依据。
  • iPhone 应用在 iPad 或 Mac 镜像环境中仍可能运行在 phone idiom 下,因此 phone idiom 与窄屏布局不再等价。
  • orientation 同样不再可靠。在可调整尺寸环境中,支持方向更接近一种偏好;即使窗口宽高比发生变化,系统仍可能保持 portrait orientation。
  • 如果需要精细控制布局,应优先使用 surrounding view size。

这也解释了我之前遇到的反直觉现象:即便窗口已经被拉宽,iPhone host 中的 horizontalSizeClass 仍可能保持为 compact。

这不是 Beta Bug,而是 Apple 有意将「宿主语义」与「可用几何空间」拆分后的结果。

horizontalSizeClass 到底还可靠吗?

答案是:可靠。

只是它可靠表达的是当前 trait environment 的粗粒度语义,而不是窗口宽度本身。

这或许也是今年最容易被误读的地方。iPhone host 中的窗口变宽,并不意味着系统一定会将 horizontalSizeClass 切换到 regular;反过来,即使是较窄的 iPad 窗口,也可能因为所处的 idiom、scene、presentation 或容器环境不同,而表现出完全不同的 trait 结果。

对于开发者而言,真正重要的变化并不是“iPhone 变成了 iPad”,而是:

  • userInterfaceIdiom:只说明应用运行在哪种宿主语义下,不应再作为布局决策依据。
  • horizontalSizeClass:是系统对 trait environment 的粗粒度上下文判断,而不是连续的宽度传感器。
  • view 或 scene 的 geometry,才是应对任意窗口尺寸、宽高比变化、浮窗以及镜像场景时更精细的布局依据。

因此,horizontalSizeClass 依然适合表达系统容器语义,例如菜单是否折叠、系统 Tab 或 Sidebar 是否可用;但对于应用自身的布局断点,例如“宽度超过多少后切换为双栏布局”或“显示侧边导航”,则应该依据当前 root view、container view 或 scene 的实际可用尺寸进行判断。

从设备到 scene,从方向到可用空间

如果只关注 WWDC 26,这一变化很容易被理解成 iPhone Resize 带来的新问题。

但如果把过去几年的线索串联起来看,会发现 Apple 实际上一直在朝着同一个方向推进:不断削弱「设备类型 + 屏幕方向」作为布局决策依据的重要性,并逐步将开发者引向 scene、trait hierarchy 与可用空间。

  • 2014:Apple 在 iOS 8 中引入 Size Class。今年的 Session 278 再次强调了这一设计理念:设备旋转本质上只是一次 bounds 的变化。
  • 2019:iPad 多窗口与 UIScene 打破了“一个 App 只有一个 UI 实例”的传统认知。Scene 开始拥有自己的生命周期、状态恢复、屏幕和几何信息。
  • 2022:iPadOS 16 的 Desktop-Class iPad 更新,将 iPad 从“放大版 iPhone”进一步推向更高密度、更接近桌面生产力的界面形态,为后续的可变空间设计奠定基础。
  • 2023:Session《Unleash the UIKit trait system》进一步明确了这一方向。环境信息被塑造成沿着 scene、window、presentation、view controller 与 view 逐层传播的上下文,而不再是单一的设备事实。这也解释了为什么开发者在 iPad window、sheet 或 split column 中读到的 horizontalSizeClass 往往与直觉不同。
  • 2024:Tab Bar 与 Sidebar 被视为同一导航层级在不同空间条件下的不同呈现形式,为后续导航形态自动切换建立了统一语言。
  • 2025:iPadOS 进一步向 macOS 靠拢,窗口、菜单栏、指针和多窗口工作流都更加桌面化。
  • 2026:iPhone 应用进入可变尺寸环境,“iPhone 应用只服务固定手机比例”的假设正式失效。

iPad 的 Full Screen 说明了同一个趋势

iPad 的 Full Screen 变化其实也在说明同一个趋势。

Apple 并没有取消用户选择全屏工作方式的权利,但正在逐步收回开发者通过 UIRequiresFullScreen 将应用锁定在旧兼容模式中的能力。

在 iPadOS 26 中,用户仍然可以通过 Settings 或 Control Center 在 Full Screen Apps、Windowed Apps 和 Stage Manager 之间进行选择;但对于开发者而言,UIRequiresFullScreen 已经被定义为 deprecated compatibility mode,并将在未来被系统忽略。

换句话说,全屏从过去的“开发者强制要求”,逐渐转变为“由用户和系统共同决定”。关于这一点,可以参考 Apple 的 TN3192UIRequiresFullScreen 文档。

这也让 iPad 的 Full Screen 与 iPhone Resize 回到了同一条主线:开发者可以表达偏好、最小尺寸以及临时方向锁定,但不应再假设自己拥有一块固定不变的画布。

回到我的项目:用 geometry 做自己的布局策略

理解这些规则之后,我开始重新审视自己的应用应该如何应对 iPhone Resize。

虽然可以根据场景 geometry,在某棵 SwiftUI 子树中注入 \.horizontalSizeClass = .regular,让部分容器进入 regular 语义,但这并不适合作为全局策略。

这种做法会影响整棵子树中所有读取该环境值的视图,而不同组件在「phone idiom + injected regular」组合下的表现并不一致。

例如,NavigationSplitView 可以展开侧边栏,但在我的测试中,TabView(.sidebarAdaptable) 并不会因此自动变成 iPad 风格的 Sidebar。

这并不意味着 UIKit 在 iOS 27 中新增的 tab sidebar opt-in API 没有价值,而是说明“宽 iPhone 窗口”与“iPad 导航环境”并不是同一个概念。

换句话说:宽窗 iPhone 仍然是 iPhone 的自适应呈现,而不是完整意义上的 iPad 产品界面。

结合自己的应用场景,我发现 Apple 在 2024 年提出的“Tab Bar 与 Sidebar 是同一导航层级的不同呈现形式”这一理念,非常适合当前项目。

因此,我为应用制定了如下策略:

  • 使用 geometry 判断当前是否进入宽屏状态。
  • 在 iPhone host 且处于宽屏时,显示自定义 Sidebar,并隐藏 Tab Bar。
  • Sidebar 中的按钮仍然通过状态驱动 Tab 切换。
  • iPadOS 下不启用这套 iPhone 专属逻辑,优先让系统组件根据自身 trait 与 geometry 自适应。
  • macOS 端彻底舍弃 Tab 结构,直接使用 Sidebar 导航。

固定画布时代结束了

未来,开发者能够表达的将更多是偏好,而不再是对界面的绝对控制。

例如:

  • 使用 UISceneSizeRestrictions 表达 scene 的 preferred minimum size。
  • 在 SwiftUI 中通过 windowResizability(_:) 与内容最小尺寸表达窗口偏好。
  • 使用 prefersInterfaceOrientationLocked 表达临时方向锁定需求。
  • 通过 windowScene(_:didUpdateEffectiveGeometry:) 观察 scene geometry 变化。
  • 使用 UIWindowSceneGeometry.isInteractivelyResizing 或 SwiftUI 的 onInteractiveResizeChange(_:) 区分交互式调整过程与最终状态。

用户拥有了最终的呈现选择权,而开发者则需要提前考虑各种可能出现的展示形态。尽管越来越多系统组件具备了良好的自适应能力,SwiftUI 也显著降低了构建响应式界面的门槛,但开发者需要面对的场景实际上变得更多了。

固定画布时代正在结束,而围绕可用空间构建界面的时代,才刚刚开始。

订阅 Fatbobman 周报

每周精选 Swift 与 SwiftUI 开发技巧,加入众多开发者的行列。

立即订阅