关于 capacitor-ios-resizability
帮助 Capacitor iOS 应用适配 iPad 等设备上的可调整窗口。它审查网页布局、原生代码、插件和应用配置,再指导修复与验证。
主要功能
- 01审查网页布局假设
- 02扫描原生 API 和插件代码
- 03检查 Info.plist 与场景设置
- 04指导跨应用层的获批修复
- 05在模拟器中验证窗口缩放
适用场景
- 01为 iPad 多任务处理做准备
- 02排查窗口缩放后失效的布局
- 03检查分屏中的插件叠加层
- 04处理启动屏幕和方向警告
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cap-go/ capgo-skills --skill capacitor-ios-resizability安装后在 Claude Code 中输入/capacitor-ios-resizability确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.jsonreferences/info-plist-and-scenes.mdnative-replacements.mdplugin-audit.mdswiftui-in-plugins.mdweb-layer.mdscripts/scan-legacy-apis.sh详细介绍
Capacitor iOS 应用的可调整大小窗口
Capacitor 应用是在 CAPBridgeViewController 中的全窗口 WKWebView。Web 视图会随窗口变化。我们需要让 Web 布局响应宽度变化,并移除那些假定只有一个全屏窗口的原生代码(应用、自定义代码、插件)。
适用场景
在以下情况触发:
- 为 iPad 多任务处理、Stage Manager、窗口化 iPad 应用、可折叠 iPhone 或外接显示器准备应用
- 窗口调整大小时,布局出错或没有更新
- 分屏视图中的插件原生叠加层(扫描仪、相机预览、自定义面板)尺寸或位置不正确
- Xcode 或 App Store Connect 警告
UIRequiresFullScreen、缺少 iPad 方向设置或启动屏幕要求 - 用户要求移除
UIScreen.main、userInterfaceIdiom、interfaceOrientation或 AppDelegate 生命周期用法
不要用于:
- 仅设置 CSS
env(safe-area-inset-*)和刘海屏内边距:safe-area-handling - 将 AppDelegate 迁移到 SceneDelegate:
capacitor-uiscene-migration(Xcode 27 的前置步骤) - 键盘遮挡:
capacitor-keyboard - 仅使用 Tailwind 断点:
tailwind-capacitor
制定计划所依据的事实
- iOS 27 会忽略
UIRequiresFullScreen,并仍会调整场景大小(TN3192)。切勿悄悄删除此键:删除后,应用会立即在当前 iOS 上变为可调整大小。切勿自行添加UIRequiresFullScreenIgnoredStartingWithVersion:它的值决定哪些版本继续采用旧行为。两者都应由用户在测试后决定。 - iOS 27 SDK 要求声明启动屏幕。 如果没有声明,上传会因
ITMS-90870被拒。Capacitor 模板会设置UILaunchStoryboardName;请保留它。 - Xcode 27 要求使用场景生命周期。 Capacitor 8.5 模板包含
SceneDelegate.swift。如果没有它,AppDelegate 生命周期方法将是唯一信号,这在多窗口场景下并不正确。如果缺少UIApplicationSceneManifest,请先运行capacitor-uiscene-migration。 - 保留
UIApplicationSupportsMultipleScenes = false。 每个场景都会创建自己的CAPBridgeViewController,以及第二个 WebView 和一组插件。Capacitor 在 8.5 中添加了场景级通知,供插件做好准备,但模板不支持多窗口。单场景也可以调整大小。 CAPBridgeViewController仅根据 iPhone 键UISupportedInterfaceOrientations构建supportedInterfaceOrientations,不会读取UISupportedInterfaceOrientations~ipad(Capacitor 8.5 源码)。即使 iPad 声明了全部四种方向,桥接层掩码仍可能将其锁定。请报告此问题;只有在用户希望采用不同的 iPad 行为时,才在桥接子类中重写supportedInterfaceOrientations。- Web 层会自动收到调整大小事件。
WKWebView会随窗口调整大小并触发resize。布局出错是因为布局代码读取了设备信息,而非视口信息。 - 插件属于第三方代码。 安装时对
node_modules的修改会丢失。请报告插件问题、升级插件、向上游提交问题,或仅在用户批准后将patch-package作为临时方案。
参考资料
仅在涉及相应主题时加载参考资料。
| 文件 | 加载时机 |
|---|---|
| references/info-plist-and-scenes.md | 检查 Info.plist 键、方向掩码、场景生命周期,以及不再触发的 AppDelegate 方法 |
| references/web-layer.md | 审查并修复 CSS/JS 布局:视口、单位、设备检测、安全区域在运行时发生变化的情况 |
| references/native-replacements.md | 在 Swift/ObjC 中替换 UIScreen.main、方向、设备类型、关键窗口、生命周期和硬编码内边距 |
| references/plugin-audit.md | 扫描 node_modules 中的插件、分类处理,以及插件作者应进行的更改 |
| references/swiftui-in-plugins.md | 插件或应用展示了必须调整大小的原生 SwiftUI 屏幕;iOS 27 SDK 的 SwiftUI 变更 |
脚本:scripts/scan-legacy-apis.sh 会扫描 ios/App/App 和所有 node_modules/**/ios 文件夹,并打印与调整大小相关的 Info.plist 键。只读。
工作流
1. 前提检查(只读)
读取 ios/App/App/Info.plist、AppDelegate.swift、SceneDelegate.swift、@capacitor/ios 版本,以及 TARGETED_DEVICE_FAMILY 构建设置(1,2 = iPhone 和 iPad)。报告:
| 检查项 | 符合条件 | 否则 |
|---|---|---|
| 启动屏幕 | 存在 UILaunchStoryboardName 或 UILaunchScreen | 报告 ITMS-90870 风险 |
| 场景生命周期 | 存在 UIApplicationSceneManifest,且 SceneDelegate.swift 属于该目标 | 先交由 capacitor-uiscene-migration 处理 |
| iPad 方向 | UISupportedInterfaceOrientations~ipad 列出了全部四种方向,或不存在 iPad 键 | 指出缺少哪些方向。提及上文的 bridge 掩码事实 |
| 全屏退出选项 | 不存在 UIRequiresFullScreen | 报告 iOS 27 会忽略此项。更改前先询问用户 |
2. 扫描
bash <skill>/scripts/scan-legacy-apis.sh .
同时扫描 Web 代码(references/web-layer.md):
grep -rnE 'screen\.(width|height|availWidth)|window\.orientation|orientationchange|\b100vh\b|navigator\.userAgent.*(iPad|iPhone)|isPlatform\(.(ipad|tablet|iphone)|innerWidth' src --include='*.{ts,tsx,js,jsx,vue,svelte,css,scss}'
创建 TODO 列表,每个文件对应一条。大型项目:每批处理 3 至 5 个文件。列表中的每个文件都必须进行更改,或书面说明无需更改的原因。
3. 报告,然后询问
将发现的问题分类为:应用 Web 代码、应用原生代码、插件(按包分类)、Info.plist。每项都说明修复方法。进行以下更改前先询问用户:
- 更改
UIRequiresFullScreen、方向键或方向掩码, - 更改共享原生代码中的公共方法签名,
- 修补
node_modules中的插件。
4. 修复
- 先处理 Web 层。这通常就是全部修复内容:references/web-layer.md。
- 应用原生代码:references/native-replacements.md。仅编辑包含目标 API 的行。保留所有
if/else分支和#available守卫。绝不将动态值替换为字面值,也绝不将一个全局对象(UIScreen.main)替换为另一个(UIApplication.shared、UITraitCollection.current、UIDevice.current)。 - 空的模板桩(例如
applicationDidBecomeActive,只有注释)无害。仅在其中包含代码时才移动。 - 插件:references/plugin-audit.md。
5. 验证
npx cap sync ios
xcrun simctl list devices available | grep -i ipad
npx cap run ios --target <iPad-simulator-UDID>
- 在 iPad 模拟器(iPadOS 26+)中,将应用切换到窗口模式并拖动窗口角落。检查窄、中、宽三种尺寸。也尝试与另一个应用使用分屏视图。
- 在每种尺寸下旋转设备。在窄窗口中打开并关闭键盘。
- 检查插件提供的原生浮层(扫描仪、相机预览、选择器)在窄窗口中的表现。
- 将应用切换到后台再切回前台。JS
pause/resume必须触发。 - 如果 Xcode 提供可折叠 iPhone 模拟器,请以相同方式测试折叠和展开。
- 重新运行扫描。对每个剩余命中项,都必须书面说明理由(例如相机拍摄方向或分析用途等非布局用途)。
错误处理
| 症状 | 原因 | 修复方法 |
|---|---|---|
| 调整大小后,布局仍停留在旧尺寸 | JS 在启动时缓存了 window.innerWidth / screen.width,或 CSS 使用了 100vh | 在 resize 时读取尺寸,或使用 CSS;将 100vh 替换为 100dvh |
| 仅在某些窗口尺寸下,内容位于状态栏或主屏幕指示器下方 | 安全区域内边距只读取了一次,或将一侧的内边距镜像到了两侧 | 在 CSS 中按边使用 env(safe-area-inset-*);切勿在 JS 中缓存 |
| 插件覆盖层在分屏视图中覆盖了错误区域 | 插件根据 UIScreen.main.bounds 调整视图尺寸 | 更新插件,或使用扫描行向上游提交问题 |
| App 已启动,但在 iOS 26 上无法调整大小 | UIRequiresFullScreen = true | 在用户测试后将其移除之前,这属于预期行为 |
上传因 ITMS-90870 被拒绝 | 缺少启动屏幕键 | 恢复 UILaunchStoryboardName,并设置为 LaunchScreen.storyboard |
添加 SceneDelegate 后,pause / resume 不再触发 | AppDelegate 生命周期方法中的自定义代码 | 将代码移至场景方法,或监听通知(参考资料) |
相关技能
safe-area-handling:CSS env() 变量和状态栏覆盖层capacitor-uiscene-migration:采用 SceneDelegate,这是进行场景感知修复的前提tailwind-capacitor:Tailwind 中的响应式断点capacitor-testing:模拟器和设备验证流程capacitor-plugin-upgrade-v8-to-v9:插件修复需要新主版本时
评论
支持 Markdown