Enhancing your camera experience with capture controls
Photos & Camera 进阶 2m

用 Capture Controls 增强相机体验

Enhancing your camera experience with capture controls

2025年6月9日

在 Apple 官方观看视频

一句话判断

物理按键拍照 + AirPods 远程快门 + Camera Control 的自定义设置调节——三个 API 组合起来,你的第三方相机 app 可以做到和系统相机一样”手不离键”的操作体验。

这场 Session 讲了什么

这场 Session 分两部分,前半部分由 Vitaliy 讲物理按键捕获(AVCaptureEventInteraction),后半部分由 Nick 讲 Camera Control(AVCaptureControl)。

AVCaptureEventInteraction 覆盖了所有物理按钮的捕获事件:音量下键/Action Button/Camera Control 触发 primary action,音量上键触发 secondary action。每个事件有 began/cancelled/ended 三个 phase,ended 是实际触发拍照的时机。SwiftUI 通过 onCameraCaptureEvent modifier 接入,6 行代码就能启用。

iOS 26 的重大更新是 AirPods 按柄远程捕获。支持 H2 芯片的 AirPods,用户可以在设置中配置”按一次”触发捕获。已接入 API 的 app 自动获得此功能。新增音效反馈 API,默认播放提示音,可以自定义或禁用。shouldPlaySound 属性只在 AirPods 触发时为 true。

AVCaptureControl 覆盖了 iPhone 16 的 Camera Control。支持三种控制类型:连续滑块(如变焦,支持全范围 zoom factor)、离散滑块(如曝光补偿,支持特定步进值)、Picker(如闪光灯/Photographic Style,遍历命名状态列表)。系统预定义了 zoom 和 exposure bias 控件(一行代码创建),自定义控件通过 AVCaptureSessionaddControl 注册,上限由 maxControlsCount 报告。Camera Control 的轻按手势激活预览模式,双击切换设置,滑动调节值,再次轻按确认。控件通过 action callback 响应值变化,可以在指定 queue 上执行以同步捕获状态。

值得深挖的点

Primary vs Secondary Action 的设计意图与实际影响

AVCaptureEventInteraction 的双 action 设计不是简单的”两个按钮两个功能”。Primary action(音量下键、Action Button、Camera Control)和 secondary action(音量上键)的分离,实际上是在鼓励开发者实现两层捕获体验:最常用的操作绑定到 primary,次要操作绑定到 secondary。

在系统相机中,primary 是拍照、secondary 是开始/停止视频录制。但你的 app 可以自由定义。比如一个专业的摄影 app 可以把 primary 设为快门,secondary 设为对焦锁定。关键是:同一个按钮不能同时触发两个 handler,系统确保了互斥。

这个设计的 trade-off 在于灵活性与一致性的平衡。如果你的 app 重新定义了 primary/secondary 的语义,用户可能需要学习新的操作映射。建议保持与系统相机一致的映射(primary = 拍照),除非你的 app 有明确的专业场景需求。

另一个容易忽略的约束:capture event 只发送给正在使用摄像头的 app。如果 app 进入后台或没有运行 AVCaptureSession,物理按钮恢复默认系统行为(调节音量或启动相机)。这是正确的行为——用户不应该在不拍照时发现音量键失效了。

Camera Control 的自定义控件设计原则

Session 给出了自定义 Camera Control 的几条设计原则,非常实用:

  1. 只影响捕获体验:Camera Control 的控件应该影响拍摄的外观或体验(变焦、曝光、滤镜),不应该控制 app 的其他功能。把不相关的功能绑到 Camera Control 上会让用户困惑。

  2. 不要为了 Camera Control 创建 capture session:运行摄像头有功耗和隐私要求,只为使用 Camera Control 而启动摄像头是不合适的。

  3. 禁用而非移除:当控件当前不可用时(比如在不支持的设备上),应该 disable 而不是 remove。移除会导致其他控件被选为 fallback,用户会疑惑控件去哪了。

关于控件数量上限,maxControlsCount 是硬限制,canAddControl 在达到上限时返回 false。实际建议是控制在 3-5 个以内——Camera Control 的交互是轻按切换 + 滑动调节,太多控件会让切换操作变得繁琐。

Trade-off:系统预定义的 zoom 和 exposure bias 控件性能最优(直接操作设备属性),自定义控件需要在 action callback 中自己处理逻辑,性能取决于你的实现。如果你的自定义控件需要频繁同步 UI(比如实时预览滤镜效果),要注意 callback queue 的选择——用 session queue 做状态管理,用 main queue 做 UI 更新。

代码片段

1. SwiftUI 启用物理按键拍照

场景:6 行代码让音量键触发拍照。

import AVKit

struct CameraView: View {
    @Bindable var model = CameraModel()

    var body: some View {
        CameraPreview(session: model.session)
            .onCameraCaptureEvent { event in
                if event.phase == .ended {
                    model.capturePhoto()
                }
            }
    }
}

坑:如果没实现 secondary handler,音量上键也会触发 primary action。如果你需要区分两个按钮的行为,必须同时实现 primary 和 secondary handler。

2. AirPods 远程捕获 + 自定义音效

场景:自定义 AirPods 按柄触发的快门音效。

.onCameraCaptureEvent { event in
    if event.shouldPlaySound {
        // 只在 AirPods 触发时自定义音效
        event.defaultSoundDisabled = true
        event.playSound(named: "cameraShutter")
    }
    if event.phase == .ended {
        model.capturePhoto()
    }
}

坑:shouldPlaySound 只在 AirPods stem click 触发时为 true。如果你的 app 用其他方式触发捕获,不会播放自定义音效。不要在这里放拍照的核心逻辑。

3. 添加自定义 Picker 控件

场景:让用户通过 Camera Control 切换滤镜效果。

let effects: [(String, CIFilter)] = [
    ("None", CIFilter()),
    ("Chrome", CIFilter(name: "CIPhotoEffectChrome")!),
    ("Noir", CIFilter(name: "CIPhotoEffectNoir")!)
]

let picker = AVCaptureIndexPicker(
    title: "Effect",
    symbolName: "camera.filters",
    indexRange: NSRange(location: 0, length: effects.count),
    actionQueue: sessionQueue
) { index in
    // 在 session queue 上应用滤镜
    currentEffect = effects[index].1
}

session.addControl(picker)

坑:addControl 前先检查 canAddControl,避免超过 maxControlsCount。另外,如果 Picker 的 selected index 对应的功能当前不可用,应该 disable 控件而不是 remove。

最佳实践

  • 保持 primary action 的语义与系统相机一致(拍照),降低用户学习成本。
  • 必须响应所有收到的 capture event——不处理会导致按钮完全失效,这是糟糕的用户体验。
  • 对 AirPods 远程捕获提供适当的音效反馈,让用户知道捕获命令已被识别。
  • 使用系统预定义的 zoom 和 exposure bias 控件,一行代码即可获得与系统相机一致的行为。
  • 自定义控件只影响捕获相关的功能,不要绑定到 app 的其他功能。
  • 用 session queue 管理设备状态,main queue 更新 UI,避免并发问题。

还有什么值得关注

  • AirPods 远程捕获需要 iOS 26 和支持 H2 芯片的 AirPods,用户需在设置中配置触发方式。
  • Camera Control 的 Lock Screen 启动功能需要实现 Lock Screen capture extension,详情参见 WWDC24 相关 session。
  • Camera Control 仅支持 iPhone 16 及以后的设备,controlsSupported 属性可以检查当前设备是否支持。
Photos & Camera