用 Capture Controls 增强相机体验
Enhancing your camera experience with capture controls
2025年6月9日
一句话判断
物理按键拍照 + 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 控件(一行代码创建),自定义控件通过 AVCaptureSession 的 addControl 注册,上限由 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 的几条设计原则,非常实用:
-
只影响捕获体验:Camera Control 的控件应该影响拍摄的外观或体验(变焦、曝光、滤镜),不应该控制 app 的其他功能。把不相关的功能绑到 Camera Control 上会让用户困惑。
-
不要为了 Camera Control 创建 capture session:运行摄像头有功耗和隐私要求,只为使用 Camera Control 而启动摄像头是不合适的。
-
禁用而非移除:当控件当前不可用时(比如在不支持的设备上),应该 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属性可以检查当前设备是否支持。