独书先生 Menu

Viewing all items for tag ios

适配 iPhone Duo,不应该从判断设备型号开始

我做了一个包含 12 个可运行示例的 SwiftUI 自适应布局项目

适配 iPhone Duo,不应该从判断设备型号开始

做 iOS 布局时,我们很容易写出这样的逻辑:

如果是手机,显示单栏;如果是平板,显示双栏;如果屏幕足够宽,再换成另一套布局。

这种做法在设备形态比较固定时似乎能够工作。但随着横竖屏切换、分屏多任务、动态字体、多窗口,以及 iPhone Duo 带来的不同姿态出现,“设备型号”越来越不能准确描述应用当前拥有的空间。

同一台设备上的同一个应用,可能占据整个屏幕,也可能只得到一个狭窄窗口。它可能处于展开状态,也可能出现需要避让的分隔区域。即使外部屏幕尺寸没有变化,安全区域和可用空间也可能已经不同。

因此,真正应该驱动布局的,不是“用户正在使用哪台设备”,而是:

系统和父容器此刻给了这个界面多少可用空间?

这也是我创建 DuoKit 的原因。

GitHub:https://github.com/openHacking/DuoKit
duokit-demo

DuoKit 是什么?

DuoKit 是一个开源的 SwiftUI 示例项目,集中展示自适应布局、导航、安全区域、折叠区域避让、多任务和多场景设计。

它目前包含 12 个可以直接运行的示例。

但需要特别说明:DuoKit 不是一个 UI 框架。

它不会提供一组需要集成到业务里的自定义组件,也不希望开发者依赖某套私有抽象。它更像一本可以运行的 SwiftUI 手册:打开一个示例,修改窗口宽度或交互状态,就能直接观察布局如何变化。

每个示例都围绕一个具体问题组织:

  • 要解决的问题是什么
  • 推荐采用什么方式
  • 涉及哪些 SwiftUI API
  • 常见错误是什么
  • 应该测试哪些边界情况
  • 对应的源码和官方资料在哪里

我希望它解决的不是“复制一段代码”,而是“理解这段代码为什么这样写”。

12 个示例,覆盖自适应界面的完整路径

DuoKit 的示例从基础布局开始,逐步进入 iPhone Duo、多任务和真实应用场景。

1. Adaptive Layout:响应可用空间

第一个示例组合使用:

  • horizontalSizeClass
  • GeometryReader
  • ViewThatFits
  • AnyLayout

它展示同一个界面如何随着容器宽度变化,在紧凑布局和展开布局之间自然切换。

这里最重要的不是某个具体断点,而是区分两个问题:

尺寸类别适合决定界面的整体层级,容器几何信息则适合处理局部排布。

如果只是通过 UIScreen.main.bounds 获取屏幕宽度,或者根据设备型号选择布局,就无法准确描述应用当前所在的窗口。

2. NavigationSplitView:只维护一套导航状态

列表与详情是最常见的自适应界面之一。

紧凑空间下,它通常表现为逐级进入的导航;空间足够时,则可以同时显示侧边栏和详情。

常见做法是分别为手机和平板维护两套导航结构。但这样很容易产生重复状态:用户在一种布局中选中了内容,切换布局后,另一套界面却不知道当前选中了什么。

DuoKit 使用 NavigationSplitView 和统一的 selection,让系统负责导航结构的展开与收起,应用只维护一份业务状态。

3. Adaptive Grid:让宽度决定列数

网格布局不应该写成:

“手机两列,平板四列。”

更稳妥的方式是定义内容能够接受的最小宽度,然后让 LazyVGrid 根据容器空间计算列数。

这样,无论应用运行在全屏、分屏还是不同方向下,网格都能连续变化,而不是只在几个预设设备之间跳转。

4. Safe Area:不要假设边距一定对称

安全区域经常被当成一个统一的边距处理。但在不同设备形态和姿态下,leading、trailing、top 和 bottom 可能完全不同。

DuoKit 会实时展示各个方向的安全区域数值,用来说明一个很容易被忽略的问题:

不能读取左侧边距,再乘以二,推断整个可用宽度。

每一侧的安全区域都应该被独立读取和处理。

5. Fold Avoidance:只移动真正受影响的内容

如果一个关键按钮刚好跨越折叠或分隔区域,继续把它放在几何中心并不合理。

但这也不意味着整个界面都需要重新布局。

更合适的方式是判断哪些自定义控件与保留区域相交,然后只把受影响的控件移动到最近的可用区域。

DuoKit 的 Fold Avoidance 示例使用一个明确标注的模拟折叠区域,对比按钮避让前后的表现。

6. Reserved Regions:理解分隔与遮挡

保留区域并不只有一种。

有些区域代表显示空间之间的分隔,例如折叠位置;另一些区域则可能代表摄像头等造成的遮挡。

DuoKit 将 division 和 occlusion 两类区域分别可视化,帮助开发者理解:屏幕矩形存在,不代表其中每个位置都适合放置交互控件。

7—9. 把导航和展示交给系统

接下来的三个示例分别展示:

  • Adaptive Toolbar
  • Adaptive TabView
  • Sheets & Popovers

它们背后的原则是一致的:优先使用系统提供的语义化容器。

例如,通过 ToolbarItem 的 placement 描述一个操作的作用,而不是用固定坐标绘制自己的工具栏;使用标准 TabView 描述导航目的地,而不是维护一套固定位置的自定义标签栏。

系统知道当前窗口和设备状态,也更有机会在环境变化时正确调整这些控件。

10. Split View Multitasking:设备没变,窗口已经变了

在分屏多任务环境中,应用拿到的可能只是显示区域的一部分。

因此,“这是一台大屏设备”并不代表应用当前有足够空间显示大屏布局。

DuoKit 使用同一个 Dashboard,在窄、中、宽三种空间中连续变化。开发者可以拖动宽度,直接观察信息层级和组件排列如何调整。

这比只在两个固定模拟器上分别截图,更容易暴露中间尺寸的问题。

11. Multiple Displays & Scenes:失败也是界面状态

多场景设计不能假设第二个场景一定能够创建。

系统可能不支持当前操作,场景激活也可能失败。因此,应用除了处理成功状态,还需要明确展示能力是否可用,并正确处理错误和回退。

DuoKit 使用一个演示者与观众视图的状态机,展示场景创建、内容同步和失败处理。

12. DuoNotes:把前面的原则放进一个完整应用

单独的 API 示例很容易理解,但真实应用的问题通常出现在多个能力组合之后。

因此,最后一个示例是一个可以创建、选择、编辑和删除内容的笔记应用 DuoNotes。

它组合使用了:

  • NavigationSplitView
  • Binding
  • toolbar
  • sheet
  • 安全区域处理
  • 无障碍支持

DuoNotes 不会为紧凑布局和展开布局分别维护两份笔记数据。无论导航如何变化,笔记列表和当前选择都只有一个事实来源。

这也是自适应应用中非常重要的一条原则:

布局可以变化,业务状态不应该因为布局变化而被复制。

DuoKit 坚持的四个设计原则

把这些示例放在一起后,可以归纳出四条共同原则。

第一,根据可用空间布局,不根据设备型号布局

设备型号只是一个间接信号,而且经常不准确。

真正与布局有关的是当前窗口、父容器、安全区域和场景状态。

第二,优先使用系统容器

NavigationSplitView、TabView、toolbar 和系统 presentation API,通常比固定坐标和自定义外壳更能适应环境变化。

自定义界面应该集中在真正需要自定义的内容上。

第三,只调整受到影响的部分

出现折叠区域或遮挡区域时,不要立刻重建整个页面。先判断哪些内容真的发生相交,再移动相关控件。

局部调整通常比全局特判更稳定。

第四,把连续变化纳入测试

不要只测试“最窄”和“最宽”两个端点。

拖动窗口宽度时,中间状态可能出现文字截断、按钮重叠、网格跳动和导航丢失。还需要同时测试:

  • 横屏与竖屏
  • 分屏窗口
  • 深色模式
  • 辅助功能字体
  • 不同安全区域
  • 场景创建失败
  • 不同设备姿态

自适应布局不是几个静态尺寸,而是一段连续变化的区间。

关于模拟功能的边界

DuoKit 当前使用 Xcode 26.6 构建,部署目标为 iOS 26.0。

其中 Fold Avoidance、Reserved Regions 和 Multiple Displays & Scenes 涉及的部分原生能力需要 iOS 27.1 SDK。为了让开发者现在也能理解相关概念,项目提供了明确标注的模拟环境。

这些模拟区域只是教学工具,不代表真实硬件尺寸,也不应该被当成 iPhone Duo 的几何数据。

采用 Xcode 27.1 后,应当在示例边界处将模拟数据替换为系统原生 API,并在真实的设备姿态环境中重新验证。

我认为这种限制必须直接写出来。教学示例可以模拟一个概念,但不能把模拟结果包装成硬件事实。

如何运行

DuoKit 没有第三方依赖,不需要账号,也不需要私有配置。

运行步骤如下:

  1. 安装 Xcode 26.6 或更高版本
  2. 克隆项目仓库
  3. 打开 DuoKit.xcodeproj
  4. 选择 DuoKit Scheme
  5. 选择 iPhone 或 iPad 模拟器并运行

项目采用 MIT License,所有示例都可以自由阅读、修改和使用。

写在最后

DuoKit 表面上是在介绍如何为 iPhone Duo 设计界面,但它真正讨论的是一个更长期的问题:

当设备和窗口不再只有几种固定形态时,我们应该怎样组织 SwiftUI 应用?

答案不是继续增加设备判断,也不是为每一种形态维护一套界面。

更可靠的方向是读取系统提供的环境,根据当前获得的空间组织内容,把导航和展示交给标准容器,并保证业务状态独立于布局存在。

如果你正在开发 SwiftUI 应用,欢迎试用 DuoKit,也欢迎提交新的示例、边界情况或改进建议。

项目地址:

https://github.com/openHacking/DuoKit

ios 后台appstoreconnect打不开

需求

在移动开发者群里,经常有群友反馈说iOS开发者后台(也就是https://appstoreconnect.apple.com/apps)很难打开,有时候重启电脑或者换浏览器可以解决,但是并不稳定。

分析

小编最近也刚接触iOS相关的开发内容,于是打开iOS后台查验究竟,确实打不开,习惯性打开控制台,发现报错了。

看报错具体内容,是一个js文件没加载成功,属于静态资源引用错误,于是打开一个报错地址

https://appstoreconnect.apple.com/access/static/assets/react.production.min.js?1.0.5

打不开,于是我把地址参数去掉

https://appstoreconnect.apple.com/access/static/assets/react.production.min.js

发现可以访问了。这就好办了,我们只需要将错误的地址重定向到正确的地址就可以了。

原文:https://appsoftea.com/zh/ios-appstoreconnect-error/

解决

我们去谷歌商店随便找一个重定向插件(可以搜索关键词 Redirect Request),比如我用的这个 https://app.requestly.io/,配置重定向规则:
1. 将https://appstoreconnect.apple.com/access/static/assets/react-dom.production.min.js?1.0.5重定向到https://appstoreconnect.apple.com/access/static/assets/react-dom.production.min.js

2. 将https://appstoreconnect.apple.com/access/static/assets/react.production.min.js?1.0.5重定向到https://appstoreconnect.apple.com/access/static/assets/react.production.min.js

然后刷新下 https://appstoreconnect.apple.com/apps 就可以正常打开了。不过过了一段时间,加了后缀的?1.0.5的链接又可以正常打开了,这可能也是为什么有时候能打开有时候打不开的原因,猜测是苹果后台静态资源缓存策略有点问题。不过通过重定向是可以解决的。

总结

以上就是小编在打开iOS开发者后台时候遇到的问题的解决办法,但是不太清楚打不开后台是不是总是这个原因,还需要后续观测下,有更多的信息欢迎和小编交流。

如何设置 iOS 开发环境

介绍

移动互联网时代,掌握 iOS 开发和安卓开发 还是很有好处的,小编最近在学习移动端开发的时候,最先遇到的问题就是环境安装问题了。

Continue reading…