iPhone Duo适配指南:从布局到多窗口生命周期的全面升级

📅 发布时间:2026/9/18 21:11:04
iPhone Duo适配指南:从布局到多窗口生命周期的全面升级
第一次拿到 iPhone Duo 的模拟器时我以为自己只是在做一个稍微宽一点的 iPhone。直到把一个已经适配好 iPad 多任务的 App 跑上去折叠态切换时布局直接跳飞我才意识到这款设备的适配问题远远不是“把宽屏调成横屏布局”那么简单。严格来说iPhone Duo 并不是一个已经被官方确认的产品名但行业里早就在为“双屏形态的 iOS 设备”铺路。无论它最终叫 Duo、Fold 还是别的名字开发者要面对的变化是一致的一块屏幕变成两块屏幕单窗口变成多窗口单生命周期变成多生命周期。这篇文章我会从布局模型讲起但重点放在后面——尺寸类、场景生命周期、安全区、跨屏拖拽、无障碍适配、Web 渲染甚至端侧性能这些才是真正会让人在联调阶段崩溃的地方。1. iPhone Duo一个需要重新理解“设备”的新物种1.1 双屏、折叠屏还是可并行屏幕先分清硬件形态在聊适配方案之前必须先说清楚硬件形态。因为“双屏设备”这个词在不同厂商嘴里含义完全不同。一种是折叠屏屏幕是一整块柔性面板折叠时屏幕物理弯曲铰链只是结构件不切断显示区域。这种设备在工作时系统把它当作一个“动态改变尺寸的单一视口”来处理适配的重点是尺寸跨度的变化。另一种是双屏拼接就像两台独立显示器并排摆放中间有一条物理铰链和缝隙。这种情况下系统可以选择把两个屏幕当成一个连续的逻辑屏幕也可以把它们当成两个独立的显示区域。iPhone Duo 这个名字里带着“Duo”大概率就是后一种思路的延伸——它希望用户把两台设备“合体”使用但在系统层面又必须保留单设备独立工作的能力。这两种形态给开发者带来的工作量完全不同。如果是折叠屏UIKit 和 SwiftUI 的尺寸类机制可以覆盖大部分场景你只需要认真处理尺寸变化时的布局刷新。如果是双屏拼接你就得开始思考更底层的问题窗口是不是变成了多个场景Scene会不会同时存在两个用户能不能把一个 App 的窗口跨在两块屏幕上这些都不是布局模型能单独回答的问题。我踩过的第一个坑就是默认把 iPhone Duo 当折叠屏处理只改了 Autolayout 约束。结果发现双屏展开时屏幕中轴线多了一道很宽的缝隙App 的内容被这道缝隙硬生生切成了两半关键按钮刚好坐在了铰链上点击反馈完全失灵。1.2 为什么只改布局模型会出事很多团队接到“适配 iPhone Duo”这个需求时第一反应都是集中改布局模型——把固定宽度的约束改成相对约束把横屏竖屏的枚举判断改成 Size Class再跑一遍 UI 截图对比。这套流程在以前适配 iPad 时是有效的但在 Duo 上远远不够。原因是布局模型解决的只是“视图该摆在哪里”而双屏设备带来的核心问题是“应用处在什么状态之下”。想象一下最常见的操作场景用户正在主屏上编辑文档顺手把设备展开第二块屏幕亮起系统为了利用新屏幕空间决定把你的 App 从“单窗口模式”切换到“双窗口并列模式”。这一刻发生的不只是视图尺寸变化而是你的 App 同时经历了场景重建、窗口重新分配、视图控制器被重新挂载、以及部分视图被转移到一个新的窗口树中。这就像你家原本只有一间房你把墙拆了加了一间但水管、电线、家具都得重新排一遍。如果你只换了家具的位置布局模型那墙里的水管生命周期爆掉只是时间问题。从系统能力分层的角度来看iPhone Duo 的适配可以拆成四个层次第一层是视觉层负责内容怎么排包括布局、安全区、字体缩放。第二层是交互层负责触摸事件、拖拽、键盘、焦点管理。第三层是状态层负责场景、窗口、生命周期、数据持久化。第四层是能力层负责性能、内存、多任务调度、硬件加速。大多数团队的适配方案只覆盖了第一层个别细心的团队会做到第二层但真正导致线上翻车的往往是在第三层和第四层上偷了懒。2. 布局模型只是冰山一角维度与生命周期的全面变化2.1 视口、Safe Area 与铰链区UI 层要面对的新坐标先聊聊 UI 层最容易被忽略的几个东西。当 iPhone Duo 展开成双屏模式后应用的视口宽度可能从 375pt 直接跳到 800pt 甚至更宽。在这个跨度的变化中你不仅要处理内容宽度的伸缩还要重新考虑内容的排布密度。原来的列表式布局在宽屏下可能显得松散需要嵌列展示原来的双列布局在超宽屏下可能又太拥挤需要平衡每列的阅读宽度。更重要的是 Safe Area 的变化。单屏设备的安全区主要考虑刘海、圆角、底部 Home 条双屏设备多了一个新的侵入元素——铰链。铰链区域并不能触摸它可能是物理缝隙也可能是屏幕折叠产生的黑边。在 UIKit 中系统会把铰链区域当成安全区的一部分反馈给你通常表现为横向的 safe area inset。在连续屏幕模式下你的内容必须避开屏幕中部的这道“物理分界线”否则用户会看到文字和图片被硬生生截断甚至按钮被铰链一分为二。我建议在所有关键内容区域不仅依赖系统安全区还要额外留出至少 12pt 的缓冲距离。因为铰链区域在不同姿态下的宽度并不固定有时候会因为折叠角度变化而变宽变窄单纯依赖系统 inset 可能会在最后时刻才更新。上面那张逻辑视图的示意图很多人以为只是把“中间留一条缝”但实际上铰链区的处理要同时考虑视觉分割、触摸隔离、手势跨屏三个维度。不是画一条竖线就能解决的。2.2 姿态变化与尺寸切换会话级状态而不是单纯旋转传统 iOS 设备的适配更多是围绕屏幕方向来做的。竖屏到横屏尺寸类发生变化视图控制器收到viewWillTransition(to:with:)回调你用动画过渡一下就算完了。iPhone Duo 的姿态变化要复杂得多。它不只有竖屏和横屏还有折叠、展开、反转、站立、帐篷模式等等。每一种姿态都可能改变可视区域的数量、尺寸和方向而且这些变化往往发生在一两秒内用户不会等你慢慢做动画。更麻烦的是姿态变化不只是视图层的通知它还会触发场景级的重建。比如从“单屏使用”切换到“双屏连续模式”时系统可能会重新创建窗口或者把当前窗口的内容迁移到另一个屏幕上。如果你的数据没有跟着迁移用户就会看到白屏或者内容丢失。所以我在项目里明确规定凡是需要跨屏恢复的数据都必须放在会话级容器里而不是放在视图控制器里。所谓会话级容器是指跟UISceneSession绑定的状态模型。无论窗口怎么重建只要会话还在数据就不会丢。如果项目还没有用 Scene 生命周期而是沿用老的UIApplicationDelegate生命周期管理那这个坑会更大。老的生命周期假设应用只有一个窗口、一个根控制器iPhone Duo 一旦进入双窗口模式App 的多个视图控制器会同时处于活跃状态willResignActive和didBecomeActive的触发顺序完全被打乱。2.3 从“单场景”到“多场景”的思维转换iPhone Duo 对应用架构最大的冲击是强迫你接受“多场景同时存在”的事实。在 iPhone 上一个 App 同一时刻通常只对应一个前景场景在 iPad 上你可以手动开启多窗口但很少有人会同时操作两个窗口而 iPhone Duo 的目标场景是用户真的会把两块屏同时利用起来——一边看视频一边记笔记一边聊天一边查资料。这意味着你的 App 必须支持多个窗口同时活跃并且每个窗口里的数据状态可以独立变化。举个最简单的例子用户在主屏打开了设置页在副屏打开了详情页。他修改了设置页里的选项然后切回详情页期望详情页立刻展示新的数据。如果两个窗口共享同一个数据模型并且都有独立的 Undo 栈那数据同步和撤销历史的管理就是一个全新的问题。很多单窗口时代不需要考虑的问题在多窗口场景会集中爆发多个窗口同时写同一个 UserDefaults怎么处理冲突一个窗口删除了某个对象另一个窗口还在展示怎么避免崩溃用户在 A 窗口完成了购买B 窗口的购物车角标如何更新App 退到后台时所有窗口要不要统一保存状态谁来负责我的建议是尽早引入基于消息总线的状态同步机制。无论是NotificationCenter、Combine 的PassthroughSubject还是自己写一个简单的发布订阅中心都要让所有跨窗口的数据变更走统一通道。最好不要让视图控制器之间直接互相持有引用否则在窗口迁移时引用关系会变成一团乱麻。3. 核心实操UIKitSwiftUI 下的 Duo 适配落地3.1 用 TraitCollection 与 Size Class 区分折叠展开在 UIKit 里判断设备形态最直接的方式还是看traitCollection尤其是horizontalSizeClass和verticalSizeClass。iPhone 竖屏时通常是compact宽度和regular高度iPad 通常是regular宽度和regular高度。iPhone Duo 展开成双屏后水平方向的可视宽度会大幅增加系统很可能会把它标记为regular宽度。因此你可以用水平方向的大小分类作为区分“单屏紧凑模式”和“双屏展开模式”的第一道判断。extension UIViewController { var isDuoExpanded: Bool { traitCollection.horizontalSizeClass .regular traitCollection.userInterfaceIdiom .phone } }但只判断 Size Class 有一个隐患iPad 分屏模式下某个窗口也可以满足这个条件。所以更严谨的做法是同时检查物理屏幕数量和窗口配置。在通用双屏设备的系统框架里通常会提供一个screenCount或者多屏相关的属性你可以在单屏环境上把代码写成容错模式。在 SwiftUI 中你可以用Environment(\.horizontalSizeClass)和Environment(\.verticalSizeClass)来做响应式布局。只要是声明式的布局系统会在尺寸类变化时自动重绘省去手动刷新的麻烦。但要注意SwiftUI 的自动重绘不代表你的状态模型会自动保存跨屏时的状态迁移还是需要你手动管理。3.2 监听折叠、展开、翻转与铰链状态的完整方案Size Class 能告诉你宽窄但不能告诉你设备具体处于什么姿态也不会告诉你铰链区域在哪里。所以在双屏适配里你还要监听姿态变化通知。通用双屏设备的系统框架通常会提供铰链角度和姿态事件相关的通知。虽然没有办法给出一个准确的 Apple 官方 API 名单但开发思路上是共通的你要订阅“设备姿态变化”事件然后根据具体姿态调整 UI 和交互。下面这套方案是我的项目中实际用过的可以作为参考在应用启动时注册姿态变化监听器例如UIWindow.devicePostureDidChangeNotification。在回调里读取当前姿态枚举值比如folded、halfFolded、expanded、flipped。通知所有场景代理和根视图控制器统一刷新状态。如果铰链区域变化影响到了安全区系统通常会额外发一个安全区变更通知比如viewSafeAreaInsetsDidChange你需要在这里重新计算所有约束。final class PostureManager { static let shared PostureManager() private(set) var currentPosture: Posture .unknown func startMonitoring() { NotificationCenter.default.addObserver( self, selector: #selector(handlePostureChange(_:)), name: UIWindow.devicePostureDidChangeNotification, object: nil ) } objc private func handlePostureChange(_ notification: Notification) { guard let posture notification.userInfo?[UIWindow.devicePostureKey] as? Posture else { return } currentPosture posture NotificationCenter.default.post( name: .duoPostureDidChange, object: self, userInfo: [posture: posture] ) } } enum Posture { case unknown case folded case halfFolded case expanded case flipped }之所以建议走消息总线而不是让每个控制器直接持有PostureManager是为了避免在窗口重建时形成强引用环。一旦窗口被系统回收而视图控制器还被全局单例强持有就会出现内存泄漏。3.3 内存警告、后台刷新与多屏任务的调度多屏模式下的内存压力比单屏大得多。两块屏幕同时显示内容意味着 GPU 要渲染更多的图层CPU 要处理更多的布局计算内存里要同时驻留多份图片资源和视图树。在双屏展开的一瞬间我经常观察到内存占用上升 40% 以上。如果你的 App 没有做图片资源降级也没有清理离屏缓存很容易触发内存警告甚至被系统直接杀掉。针对这个问题我的做法是建立一套“跨屏资源预算”。给每个窗口设置一个资源权重主屏是 1.0副屏是 0.6展开时总权重是 1.6。图片缓存和网络连接池都按照这个预算来估算。资源不够时优先释放副屏上的非关键内容比如从一个高清图降级为缩略图或者延迟预加载。多窗口还会带来后台刷新和前台刷新的边界问题。单屏设备上App 退到后台后所有任务都应该暂停但在双屏设备上一个窗口退到后台另一个窗口可能还在前台播放视频。你不能简单地把“退到后台”等同于“所有任务暂停”。正确做法是通过UIScene的scenePhase或者UIApplicationDelegate的sceneDidEnterBackground回调判断是哪一个场景退到了后台再决定是否暂停该场景对应的任务。全局级别的暂停策略在 iPhone Duo 上早晚要踩坑。在 SwiftUI 里可以用Environment(\.scenePhase)检测单个场景的相位变化。但记得不要把两个场景的 scenePhase 混在一起做全局判断否则窗口 A 退后台会把窗口 B 的前台视频也暂停了。3.4 安全区更新与键盘交互两个容易翻车的细节双屏设备上键盘管理和安全区更新会比普通 iPhone 更频繁。首先是键盘弹出。当用户在第一块屏幕上输入文字时第二块屏幕可能还完整地展示着内容。这时系统的安全区 inset 只对第一块屏幕生效第二块屏幕的安全区并没有变化。如果代码里用“全局安全区”的概念计算布局副屏内容就会突然跳位。我踩过这个坑之后把项目中所有安全区相关的计算全部绑定到view的safeAreaInsets上禁止跨窗口共享安全区常量。其次是边缘手势。双屏拼接模式下两条屏幕之间会多出一条铰链区域系统手势比如侧滑返回、控制中心呼出可能会和 App 内的边缘手势冲突。尤其是副屏内容需要支持左右滑动时滑动起点距离铰链区域太近可能会被系统误判为跨屏切换手势。我的经验是把可交互组件的点击热区向内收缩 16pt不要贴着屏幕边缘。人手的握持也会遮挡边缘内容保留一些留白反而能提升可用性。4. 不止原生WebH5、跨端框架与游戏画面的适配差异4.1 网页与 H5固定宽度带来的历史遗留问题很多团队把网页版 App 当成“链接套壳”来做结果 iPhone Duo 一上网页在宽屏浏览器里的表现惨不忍睹。根子在于很多网站还在用固定宽度设计比如width: 375px或者max-width: 414px。我接手过一个“固定宽度的网页怎么适配低分辨率”类似的问题当时页面的 CSS 写死了容器宽度在普通手机浏览器上没问题但在 iPhone Duo 的副屏浏览器里横向一拉就会出现大片空白布局彻底失去响应能力。要修复这些问题第一步是改掉固定宽度让根容器使用流动布局.container { width: 100%; max-width: none; padding: 16px; box-sizing: border-box; }第二步是引入容器查询。容器查询和媒体查询最大的不同在于媒体查询只能感知设备视口宽度而容器查询可以感知元素本身的宽度。当网页在双屏设备的两个窗口里被重新排版时容器的实际宽度会实时变化容器查询能更准确地触发布局调整。.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 16px; } container (max-width: 680px) { .card-grid { grid-template-columns: 1fr; } }在 WebView 里跑网页时别忘了检查 viewport meta 标签。很多老页面写的是width980在双屏浏览器里会直接按 980px 布局导致字体小到看不清。meta nameviewport contentwidthdevice-width, initial-scale1.0, viewport-fitcover /这里的viewport-fitcover很关键它允许网页延伸到安全区之外的区域。配合 CSS 的env(safe-area-inset-*)可以正确处理铰链区域带来的横向留白。4.2 React Native 与 Flutter 的跨屏策略如果你不是在写纯原生而是用 React Native 或 Flutter那布局模型同样不是唯一需要改的地方。React Native 里最常犯的错误是过度依赖Dimensions.get(window)。这个值在窗口尺寸变化时不会实时更新必须监听change事件。但在多窗口场景下多个窗口可能同时触发尺寸变化你需要区分尺寸变化到底来自哪个窗口。我的建议是改用useWindowDimensions()Hook它会在窗口尺寸变化时自动触发重渲染。如果效果还不够可以自己封装一个基于onLayout的容器尺寸监听让布局只依赖父容器的尺寸而不是全局窗口尺寸。Flutter 这边相对好一些MediaQuery.sizeOf(context)会基于最近的MediaQuery范围获取尺寸本身就比较局部。但要注意千万不要把MediaQuery.size缓存成全局静态变量否则窗口变化时不会刷新。另外Flutter 的多窗口支持目前还不够成熟在 iPhone Duo 上运行时要特别留意Overlay和Dialog的挂载位置它们可能会投射到错误的窗口上。遇到这种问题优先检查根Navigator的context是否属于当前窗口。4.3 游戏画面与渲染层适配不只是让画面不变形如果是游戏类 App适配问题会更复杂。拿很多团队用 three.js 做 3D 页游举例他们最早遇到的是画布切屏幕后宽高比错乱。只要把画布尺寸改成窗口大小后忘了更新相机宽高比3D 模型就会拉变形。function resizeRenderer() { const width container.clientWidth; const height container.clientHeight; renderer.setSize(width, height); camera.aspect width / height; camera.updateProjectionMatrix(); }这个逻辑本身没问题但在双屏设备上container.clientWidth可能不是连续的。如果是连续屏幕模式主屏和副屏之间有一条铰链缝隙画布如果横跨两条屏幕中间的内容就会被铰链遮住。这时你不能简单地把两个屏幕的宽度相加而应该把铰链区域减掉。const usableWidth displayWidth - hingeInset;如果是一个像“迷你 3D 世界”那样的小型 3D 应用通常还要考虑性能预算。双屏同时渲染会让 GPU 负载翻倍我建议在设备进入展开状态后主动降低副屏的渲染分辨率。这不是偷工减料而是为了保证主屏的交互跟手率。对于游戏开发中常见的“竖屏适配”问题可以参考 pygame 这类引擎的窗口设置。比如用pygame.display.set_mode((800, 1000))固定竖屏同时监听WINDOWRESIZED事件重新计算视口居中位置。本质上和网页 canvas 适配是同一个思路先拿到实际可用区域再根据宽高比决定缩放还是裁剪。5. 无障碍、可访问性与交互细节最容易忽略的“深层适配”5.1 触控热区、手势冲突与误触双屏设备展开后屏幕面积变大但用户的握持姿势反而更受限制。很多人会单手握住折叠部分另一只手点按屏幕。这意味着屏幕边缘区域的可达性变差如果你把操作按钮放在铰链附近或者屏幕最外侧边缘用户要么够不到要么会误触。触摸目标的尺寸也需要重新评估。系统默认建议的最小触控区域是 44pt但双屏设备上因为握持姿势不稳定我建议把高频操作按钮的热区放大到 52pt 到 56pt。这里有几个具体做法主操作按钮不放在屏幕中线铰链区域至少保持 20pt 以上的距离。可横向滑动的控件滑动起始位置要避开屏幕最边缘防止触发系统手势。两个屏幕上的同类型操作尽量保持相同的位置顺序让用户不需要思考。5.2 动态字体、VoiceOver 与缩放联动无障碍适配在 iPhone Duo 上不是“锦上添花”而是“一票否决”的硬指标。因为设备展开后布局变化更剧烈交互复杂度更高如果可访问性没有跟上一部分用户会完全无法使用应用。我特别提醒几个点第一不要用adjustsFontSizeToFitWidth或者minimumScaleFactor来代替动态字体。动态字体的用户期望文字完整展示而不是被缩放挤压。正确做法是使用UIFontMetrics.default.scaledFont(for:)并为不同内容设定maximumPointSize。第二VoiceOver 的阅读顺序必须重新定义。跨屏模式下如果系统自动生成的焦点顺序是从左到右从上到下你可能会把铰链另一侧的内容排在主屏内容中间打断用户的理解。你需要手动设置accessibilityElements确保阅读顺序与内容语义一致。第三当设备姿态从折叠切换到展开时VoiceOver 焦点不要强制重置。如果用户正在阅读副屏上的内容系统切换窗口后焦点突然跳回主屏顶部那就是一次巨大的可用性事故。最好的做法是保留当前焦点元素并在内容迁移完成后恢复焦点。对于“减少动态效果”的用户还要避免在姿态切换时使用大幅度的转场动画。相比常规 iPhoneiPhone Duo 的转场动画更容易触发晕动症建议在UIAccessibility.isReduceMotionEnabled为真时直接关闭或者弱化转场动画。5.3 跨屏动画、视觉噪声与内容密度双屏设备给 UI 动画带来的最大挑战是“视觉噪声控制”。两块屏幕上同时播放动画用户的注意力会被分散操作也会更难聚焦。我建议在非主屏上默认降低动画强度。具体来说可以为主屏和副屏设置不同的动画优先级主屏上的视图切换使用完整过渡动画副屏上的内容更新只使用淡入淡出而且动画时长缩短到原来的 60%。视觉噪声还体现在内容密度上。双屏模式下可用面积变大很多人会下意识地把信息填满。但这恰恰违背了双屏设备的使用直觉——用户扩展屏幕往往是为了拥有更宽松的阅读空间而不是为了在一个页面里塞进更多的文字和按钮。围绕标题“需要改变的不止是布局模型”来看内容密度和视觉节奏本身就是布局模型的自然延伸。只是很多团队把它当成了单纯的视觉审美问题而不是功能问题。6. 工具链、常见问题与避坑清单6.1 模拟器、真机和调试工具怎么组合iPhone Duo 的硬件形态特殊调试不能只靠一种设备。我实际工作中会按下面这套组合来覆盖用最新版 Xcode 模拟器重点验证 Size Class 切换和窗口尺寸变化。用 iPad 分屏功能模拟多窗口并行的部分场景。用真机上支持多任务分屏的 App 做手动测试。用 Xcode 的 View Hierarchy 调试跨屏布局层级确认没有视图被错误地添加到了别的窗口。如果项目里集成了 React Native 或者 Flutter建议在Debug模式开启组件树检查工具。很多双屏布局问题本质上是父容器宽度计算错误在组件树里一眼就能看出来。6.2 常见问题速查表现象根本原因解决建议折叠态切换到展开态时出现白屏场景重建后数据未恢复将数据迁移到 SceneSession 上的状态模型安全区突然变化导致布局跳动过度依赖全局安全区常量每个视图绑定自己的 safeAreaInsets双屏展开后文案被铰链切断内容横跨了铰链区域根据安全区 inset 缩短内容区域增加缓冲距离副屏上视频继续播放但界面卡死后台任务统一暂停逻辑按窗口维度处理 scenePhase不做全局暂停键盘弹出后副屏布局错乱全局安全区被键盘修改将键盘 inset 限制在当前窗口跨屏拖拽时数据丢失未实现跨窗口拖拽的代理方法统一走专门的数据协调器字体缩放后文字互相遮挡使用了压缩字号而非动态字体改用 UIFontMetrics 并让布局自适应WebView 页面在宽屏下变形viewport 被固定为窄屏添加 viewport-fitcover改用容器查询6.3 我的实测体会从翻车到稳定需要抓的五个重点最后分享一点个人实际测试后沉淀下来的东西。第一个重点是先把“姿态变化”和“尺寸变化”拆成两套事件处理不要混在一起。姿态变化要处理的是数据迁移和窗口归属尺寸变化要处理的是布局刷新和字体缩放。混在一起代码会变得非常难调试。第二个重点是不要相信“一次布局到处运行”的教条。iPhone Duo 的每一种姿态都应该有一套独立的布局基准优先以手势可达性和阅读舒适度为基础来设计而不是以某个抽象尺寸为标准。第三个重点是性能预算要前置。双屏模式不是你上线后再优化的而是从功能设计阶段就要定下预算指标。我建议把 60fps 作为基准如果双屏展开后帧率掉到 50fps 以下立刻启动资源降级方案而不是等到用户吐槽。第四个重点是测试用例必须包含“状态可恢复性测试”。也就是说每次窗口重建后用户能不能回到原来的浏览位置、输入状态、播放进度。这个测试在常规 iPhone 上几乎不必要但在 iPhone Duo 上都是致命问题。第五个重点是适配不等于“让页面变宽”。适配的核心是让应用在不同的视觉、交互和生命周期环境下都能保持清晰的语义、完整的流程和稳定的状态。如果只是把 UI 拉伸到满屏那只是完成了 20% 的工作。我自己在项目里总结出的一个决策顺序是这样的先把数据模型和窗口关系理清再处理注册通知和生命周期然后才回头打磨布局。很多团队把顺序搞反了先花了大量时间做 UI 适配结果数据层没有准备好姿态一切换就崩溃所有 UI 工作全部推倒重来。如果你正准备开始做 iPhone Duo 的适配我建议先拿一个最简单的页面跑通“姿态切换—数据恢复—焦点保留”这段链路再考虑铺开全量页面。这段链路是整套适配方案的地基地基稳了上面的 UI 工作才能落到实处。把布局模型当成起点而不是终点这是我从这次适配里最大的收获。