相机SwiftUIiPhone Duo

iPhone Duo 的相机与外屏

两颗前置摄像头、虚拟前置摄像头、方向协调器,以及相机捕捉配件——App 同时使用两块屏幕的唯一途径。

2026年10月4日阅读约 6 分钟8 个来源

iPhone Duo 是第一台拥有两颗前置摄像头的 iPhone,而且它的两块屏幕可以朝向相反的方向,所以“前置”不再等于“朝向拿着手机的人”。在 iOS 27.1 SDK 中,相机也是 App 同时在两块屏幕上绘制内容的唯一途径。本文根据 Apple 的相机讲座和多屏幕讲座,以及已经基于这些 API 开发过的开发者的经验,介绍这两方面的内容。

要点

  • 像往常一样请求前置摄像头,你拿到的是虚拟前置摄像头,它会替你在外侧和内侧摄像头之间切换,规格为 1080p60,没有深度信息。
  • 两颗前置摄像头报告的都是 .front。用 AVCaptureDeviceDirectionCoordinator 来了解哪颗摄像头真正朝向用户。
  • 手机展开时,你的 App 只能通过 CameraCaptureAccessory 使用外屏,而且只能在全屏状态下、只能在有活跃相机会话时使用。
  • 用 AVCaptureDeviceRotationCoordinator 保持预览画面方向正确,并让控件避开屏下摄像头的保留区域。
  • 这些在模拟器里都跑不起来。请为 10 月 23 日安排一轮真机测试。

两颗前置摄像头,外加一颗虚拟的

背面,Duo 配备了与 iPhone 18 Pro 相同的 4800 万像素融合主摄和 4800 万像素融合超广角摄像头,支持 4K 120 fps 杜比视界拍摄;把展开的手机翻过来时,它们还可以当自拍摄像头用。新的是正面。两颗前置摄像头都是方形传感器,视野为超广角。

四个朋友在户外对着镜头自拍微笑
来自 Apple 发布公告的样张。朋友们加入画面时,人物居中前置摄像头会扩大取景范围,还可以旋转画面,把所有人都框进来。图片:Apple
外侧前置摄像头 内侧前置摄像头
位置 在外屏里 在内屏下方
设备类型 .builtInOuterUltraWideCamera .builtInInnerUltraWideCamera
视频 最高 4K 120 fps 最高 1080p 60 fps

像在任何 iPhone 上一样,通过 discovery session 请求前置广角或超广角摄像头,在 Duo 上你拿到的就是新的虚拟前置摄像头。它会替你在两者之间切换:手机展开时用内侧摄像头,合上时用外侧摄像头。代价是只能取两者的共同规格:1080p 60 fps,而且没有深度信息。深度信息只能从物理摄像头获取。如果直接使用某颗物理摄像头,手机打开或合上时就要由你的 App 来负责切换。

内侧摄像头是 iPhone 上第一颗屏下摄像头,Apple 的指南说它在摄像头启用之前是隐藏的。布局系统把它视为一块保留区域:Blake Crosley 的探测 App 发现,在展开的屏幕上它被报告为一块未激活的遮挡区域。当你的会话使用它时,就要假定这块区域有影响,让控件避开它。如何读取它,见折痕指南。

前置不等于朝向你

AVCaptureDevice.position 一直只有前置和后置两种,两颗前置摄像头报告的都是 .front。在 Duo 上这就不够了。用外侧前置摄像头推流时打开手机,这颗摄像头就背对着你了。合上手机,同一颗摄像头又转回来朝向你。把展开的手机翻过来,后置摄像头就朝向你,Duo 正是这样用后置摄像头自拍的。

双手握着展开的 iPhone Duo 进行 FaceTime 通话:左半屏是后置摄像头拍到的身边的人,右半屏是通话对象
展开的 Duo 上的 FaceTime:左半屏是后置摄像头拍到的身边的人,右半屏是通话对象。哪颗摄像头“朝前”,取决于你以哪个视图为参照来询问。图片:Apple

AVKit 里的 AVCaptureDeviceDirectionCoordinator 会持续跟踪这一点。把相机界面所在的视图、你关心的设备类型和一个处理程序交给它:

directionCoordinator = AVCaptureDeviceDirectionCoordinator(
    view: view,
    deviceTypes: [
        .builtInOuterUltraWideCamera,
        .builtInInnerUltraWideCamera,
        .builtInDualWideCamera,
    ],
    changeHandler: { [weak self] map in
        self?.updateCameraSession(map)
    }
)

方向是相对于那个视图而言的。视图在外屏上时,外侧前置摄像头朝前,后置摄像头朝后。打开手机,视图移到内屏,处理程序就会触发:现在朝前的是内侧前置摄像头。

协调器绑定在一个视图上,所以它运行在主 actor 上,处理程序不应直接调用 AVFoundation。它交给你的是 AVCaptureDeviceDescriptor 值,而不是设备本身。描述符是 sendable 的,携带了创建设备所需的信息,所以把它传给拥有捕捉会话的那个 actor。在处理程序中:

  • 重新配置会话,改用现在朝前的那颗摄像头推流。
  • 当朝前的是后置摄像头时,把预览镜像,让用后置摄像头自拍也有自拍的感觉。
  • 针对切换后的摄像头更新界面。

详情见 Apple 的文章“Choosing a Camera by the Direction it Faces”。

同时使用两块屏幕

Apple 自家的相机功能展示了手机展开时外屏的用途。Duo 预览让被拍的人看到自己的取景;Kid Cue 播放《史努比》动画,让小朋友看向镜头;Duo FaceTime 让你身边的人也能加入通话,FaceTime 中的双路捕捉则同时发送前置和后置画面。你的 App 通过一个场景配件来使用这块屏幕,而目前能做到这一点的只有一种:CameraCaptureAccessory。

半折的 iPhone Duo 放在屋顶上,立起的半边屏幕上,相机 App 正框住身后摆姿势的五个人
笔记本式姿态下的相机 App。手机这样展开时,Duo 预览会在朝向被拍者的外屏上显示他们的取景。图片:Apple
struct CameraRootView: View {
    @State private var model = TeleprompterModel()

    var body: some View {
        CameraView(model: model)
            .sceneAccessory {
                CameraCaptureAccessory(isEnabled: $model.isEnabled) {
                    TeleprompterView(model: model)
                }
                .onAvailabilityChange { available in
                    model.isAvailable = available
                }
            }
            .toolbar {
                TeleprompterToggle(isEnabled: $model.isEnabled)
                    .disabled(!model.isAvailable)
            }
    }
}

这是 Apple 多屏幕讲座里的提词器示例:台词显示在外屏上,朝向被拍摄的人。随之而来的还有这些规则:

  • 只有当你的 App 在内屏上全屏运行、并且有活跃的相机会话时,这个配件才可用。分屏浏览不算,合上手机也会让它消失。
  • 把它注册在相机界面所在的同一个视图上,这样它只在那个视图显示时才出现。
  • 可用状态会在你不经意间变化。合上手机时,切换开关就是靠 onAvailabilityChange 把自己禁用的。
  • 配件是第二块屏幕上的第二个视图,所以要给它配一个单独的方向协调器。每个协调器报告的方向都相对于它自己的视图,这意味着对外屏的视图来说,后置摄像头是朝前的。

Anton Gubarenko 对 9 月小组实验室的记录补充说,App 还需要相应的 entitlement,而且光有 entitlement 并不能点亮外屏。没有相机会话,手机展开时就没有外屏可用。

ryo 开发的你比我猜游戏 CharaDUO 展示了一个创意如何遵守这条规则,并把它变成一个功能:描述者在内屏上看词,坐在桌子对面的猜词者从外屏上看到分类、倒计时和一排与答案字数相同的 emoji。因为外屏需要相机会话,游戏就给相机安排了任务:一个反应摄像头把猜词者录下来,在比赛结束时做成精彩集锦。它只在 iPhone Duo 上请求相机(通过外侧超广角摄像头或铰链报告来识别),在任何 iPhone 上用一块屏幕也照样能玩。在桌面姿态下,它还会把外屏内容旋转四分之一圈,让对面的人读到的文字是正的。

如果你的创意需要在手机展开时使用外屏,先确认相机确实是其中合理的一部分,再去做其余的部分。

铺满屏幕,保持方向正确

  • 在内屏上以后置摄像头的完整视野推流时,预览周围会留出空间。可以把预览偏移,把控件集中在剩下的空间里,或者让预览铺满屏幕。AVCaptureVideoPreviewLayer 的 videoGravity 决定预览如何填充它的图层。
  • 前置摄像头的方形传感器可以铺满宽阔的内屏。用设备的 dynamicAspectRatio 选择一个横向比例。Apple 在 WWDC26 关于人物居中前置摄像头的讲座里介绍了方形传感器。
  • 采用 AVCaptureDeviceRotationCoordinator 来保持预览和照片的方向正确。在 Duo 上,它会在你的 App 在两块屏幕之间移动时更新。用上它之后,关闭照片输出的 isCameraSensorOrientationCompensationEnabled 以提升性能。Duo 的每颗前置摄像头都默认开启了这项补偿。

Chad Etzel 的 NeoCam 也处理了折叠。手机平放时,取景器占据尽可能多的空间;手机折起时,它移进一个 ArrangementView,从而始终待在同一个平面上。

Chad Etzel@jazzychad

a cool iPhone Duo layout I've landed on for NeoCam: when completely open, the viewfinder takes up as much room as possible, but when folded, it switches to an ArrangementView to keep the viewfinder on one plane

安排一轮真机测试

模拟器无法运行捕捉会话,所以在硬件上市之前,这些都看不到实际效果。CharaDUO 的开发者用合成画面和仅限调试的覆盖设置检查了反应摄像头,并列出了尚未得到证实的部分:配件场景中的方向协调器、铰链移动时的输入切换、保持画面水平,以及长时间对局的发热。Ihor Malovanyi 为 Duo 一书写的配套代码,也同样把相机章节标注为需要真机。

把这些都列进 10 月 23 日的待办清单,还要加上外侧摄像头的真实位置。CharaDUO 发现,模拟器在外屏上的安全区域内边距与它绘制摄像头的位置并不一致。

在那之前,你仍然可以检查 App 在合上的手机上绘制了什么。模拟器为每块屏幕都有单独的帧缓冲区,CharaDUO 的 README 是这样截取外屏的:

# 列出屏幕:外屏是 1398 × 2034。
xcrun simctl io booted enumerate
xcrun simctl io booted screenshot --display=<UUID> outer.png

今天就能做的其余测试,见检查清单。

速查表

API 说明
两颗前置摄像头 .builtInOuterUltraWideCamera、.builtInInnerUltraWideCamera 方形传感器。外侧最高 4K120,内侧最高 1080p60
一颗随折叠切换的前置摄像头 虚拟前置摄像头,通过普通的前置广角或超广角 discovery 获取 1080p60,无深度信息
哪颗摄像头朝向用户 AVKit 中的 AVCaptureDeviceDirectionCoordinator 按视图区分;交给你 AVCaptureDeviceDescriptor 值
保持预览和照片方向正确 AVCaptureDeviceRotationCoordinator 然后关闭 isCameraSensorOrientationCompensationEnabled
铺满宽阔的内屏 设备的 dynamicAspectRatio 从方形传感器得到横向比例
展开时使用外屏 sceneAccessory 配合 CameraCaptureAccessory;UIKit 用 registerSceneAccessory 配合 UISceneAccessory.cameraCapture() 在内屏全屏、会话活跃、具备 entitlement
知道配件何时可以显示 onAvailabilityChange 合上手机或进入分屏浏览会让它消失
屏下摄像头的位置 reservedRegions(kind: .occlusion) 在展开的屏幕上未激活,直到有会话使用它

继续阅读

App · 规格阅读约 3 分钟

iPhone Duo 能运行 iPad App 吗?iPhone Duo 与 iPad mini

iPhone Duo 的内屏几乎有 iPad mini 那么大,但它运行的是 iOS,不是 iPadOS。这对仅限 iPad 的 App 意味着什么、为什么很多 Duo App 看起来会像它们的 iPad 版,以及 Duo 从 iPad 借来了什么。

10月6日阅读指南