你把 iPhone 举起来,对准一个逆光走来的孩子,然后用拇指点了一下人脸,接着长按屏幕锁定曝光,再上下拖动那个小太阳。等你把这个流程做完,孩子已经跑出画面了。这不是偶然的失误,而是 iPhone 相机 App 里每天都在发生的交互成本。
Camera Sun 这个在 Hacker News 上展示的项目,目标很直接——它认为系统相机应用是可以被修正的。项目标题里那句 “Fixing the iPhone's Camera App” 说明了很多:开发者愿意从 iPhone 最成熟、最常用的应用里找出不合理之处,并且重新做一款。
我的判断是,这类项目的价值不在于能拍出规格更高的照片,而在于它重新思考了“按下快门之前,用户的手到底要经历多少步”。这篇文章会从三个层面展开:原生相机 App 到底哪里有问题;一个“修复型”相机应用应该在哪些维度上做设计;以及如果你也想做同类产品,从工程角度看,真正难的部分在哪。
1. 先还原问题:iPhone 原生相机真正“缺”的,不是硬件
每次聊到 iPhone 相机,很多人的第一反应是“硬件已经很好了,还要第三方相机做什么”。这话有道理,但忽略了一个事实:硬件能力需要通过软件界面才能被用户调用。Camera Sun 这类项目针对的恰好是软件层的问题——不是相机拍不出好照片,而是用户在关键时刻“够不到”那个好照片。
1.1 自动模式很好,但“想控制一点”就变得不省心
iPhone 原生相机的默认体验确实做得很好,点按对焦、点按测光、自动曝光补偿,大部分日常场景都能应付。问题出在“想要稍微控制一下”的时候。
比如你希望以画面中某个区域作为测光基准,同时又希望焦点锁定在另一个区域。这个操作在原生相机里经常要做两三次:先点一次对焦测光位置,长按锁定,再拖动小太阳调整曝光。整个过程里,眼睛不能离开屏幕,手不能抖,否则很容易滑过头。
更麻烦的是,原生相机把对焦和测光绑定在同一点上。你很难在对焦点不变的前提下,把测光点挪到别处。一些小众第三方相机允许你对焦和测光分离,这是很多摄影爱好者愿意多装一个相机 App 的重要原因。
1.2 模式切换的成本,在关键时刻被放大了
从照片切到视频,从视频切到人像,从人像切到全景,原生系统用的是横向滑动。这个设计在平时很优雅,不占用屏幕按钮空间,但在拍摄动态场景时,问题就暴露了。
你可能正在等一个瞬间,这时右手拇指本来稳定地放在快门附近,但因为想切换到视频模式,你滑动了两次。一次可能滑过头,从照片滑到了人像,于是你又得滑回来。等你终于进入视频模式,画面里的内容已经变了。
这种成本的本质不是“滑动”本身很慢,而是“滑动不可精确预期”。你没有办法保证一次滑动就落在目标模式上。
1.3 信息面板缺失,你只能在拍完以后后悔
原生相机不是为“想看到自己在拍什么参数”的人准备的。它不显示快门速度、ISO、焦距,也没有实时直方图。网格线和水平仪默认也是关闭的,必须去系统设置里打开。
对于普通用户来说,这没什么问题。但对于想学习曝光、想确认自己是不是拍糊了的人,这个限制就会被感觉到。
你只能先拍一张,然后切到相册里查看照片信息,再切回相机重新调整。每多一次切换,就多一次错过瞬间的风险。Camera Sun 这类“修复型”项目,本质上就是在补这些一直被系统忽略的交互缺口。
2. Camera Sun 这类“修复型”项目,修复的是交互效率
我没有拿到 Camera Sun 的完整功能列表,因为这种公开项目通常还在迭代中,具体功能要以你实际下载到的版本为准。但从它的标题和这类项目的常见路径来看,它尝试做的事情是可以被理解的。
它不是要把 iPhone 变成单反,而是想把“按下快门之前”这个过程中的摩擦减到最少。
2.1 它把系统相机当成一个可以被推翻重做的产品
“Fixing the iPhone's Camera App”这句话,已经代表了开发者的立场:系统相机不是不可质疑的默认事实,而是一个可以被重新设计的软件产品。
这种心态在工程圈里很有价值,因为它意味着你愿意重新审视那些“大家都这么用”的交互。比如:
- 为什么曝光调整一定要用长按加小太阳?能不能有一根更直接的亮度调节条?
- 为什么模式切换一定要横向滑动?能不能按一个按钮直达某个模式?
- 为什么取景器上方的信息总是那样稀疏?能不能自定义显示项目?
这些都是值得重新设计的问题。注意,重新设计不等于堆功能。如果一个第三方相机只是把所有按钮都放到屏幕上,那它并没有“修复”任何东西,只是把操作从手势换成了面板,学习成本反而更高。
2.2 重构操作路径,本质是重新分配用户的注意力
很多人低估了相机应用里的“注意力成本”。拍照这个动作的核心,不是点击快门的那一下,而是整个过程中用户的目光在哪里。
在理想情况下,用户大多数时候应该盯着取景器里的内容,关注光线的变化、构图和时机,而不是在屏幕上寻找某个按钮、某个滑块,或者纠结自己刚才是不是滑错了。相机 UI 设计得好不好,只需要看一个标准:用户的眼睛离开取景器的频率有多高。
这也是为什么很多第三方相机把常用参数直接放在第一层界面,而不是藏在二级菜单里。哪怕只是减少一次点击,对于“捕捉瞬间”这件事来说都是值得的。
不过这里有个边界:交互做得越直接,界面包含的信息通常越多,对新手也就越不友好。Camera Sun 如果真做成了极简相机,那它可能牺牲的是专业参数的可操作性;如果它做成了专业面板,那它牺牲的就是极简体验。任何一种选择都不会适配所有人。
2.3 从“有趣”到“有用”,还隔着稳定和数据安全
每次在 Hacker News 上看到相机类项目,我都会多留意一下它是否解决了数据链路的问题。拍照类应用有一个特殊点:用户对它的信任建立在“照片不能丢”这个基础上。
一个相机界面设计得再漂亮,如果拍完的照片只能存在 App 自己的私有目录,导出不方便;如果拍几张就闪退;如果在后台切换时丢失了当前会话——那这个产品就没有完成“修复”的目标。
换句话说,Camera Sun 这类项目真正的分水岭,不是第一版 UI 做得多好看,而是它能不能成为一个可以天天打开、拍完放心保存、关键时刻不掉链子的工具。
3. 想动手做同类项目?从最小相机链路开始
如果你看完前面这些,也想自己做一个“修复型”相机应用,我建议先从最基础的技术链路开始。不要一上来就规划复杂的界面、滤镜、参数面板。先把“预览—拍照—保存”这条链路跑通,再谈其他。
3.1 一个能跑通的最小 iOS 相机示例
在 iOS 开发里,最常用的相机框架是 AVFoundation。一个最小可运行的结构大致是这样:
import UIKit import AVFoundation final class CameraViewController: UIViewController { private let session = AVCaptureSession() private let photoOutput = AVCapturePhotoOutput() private var previewLayer: AVCaptureVideoPreviewLayer? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) checkPermissionAndStart() } private func checkPermissionAndStart() { switch AVCaptureDevice.authorizationStatus(for: .video) { case .authorized: setupAndStartSession() case .notDetermined: AVCaptureDevice.requestAccess(for: .video) { granted in DispatchQueue.main.async { if granted { self.setupAndStartSession() } } } default: // 提示用户去系统设置中开启相机权限 break } } private func setupAndStartSession() { guard let camera = AVCaptureDevice.default( .builtInWideAngleCamera, for: .video, position: .back ) else { return } do { let input = try AVCaptureDeviceInput(device: camera) if session.canAddInput(input) { session.addInput(input) } if session.canAddOutput(photoOutput) { session.addOutput(photoOutput) } } catch { // 记录日志,检查设备输入是否被占用 return } let layer = AVCaptureVideoPreviewLayer(session: session) layer.frame = view.bounds layer.videoGravity = .resizeAspectFill view.layer.insertSublayer(layer, at: 0) previewLayer = layer session.startRunning() } }这只是一个示例骨架,它的作用是让你先看到“预览画面”。真正到保存照片,还需要实现 AVCapturePhotoCaptureDelegate,在回调里拿到照片数据并写入相册。但从工程角度,我的建议始终是:先跑到这一步,再开始加功能。
注意:不要一上来就调参数。相机应用的第一条规则是,先把最小链路跑通,再谈优化。
3.2 关键配置不是越多越好,而是要和场景匹配
很多初学者在写相机 App 时,会想把每一个可以设置的参数都开放出来。这其实是一个陷阱。参数越多,意味着你需要处理的失效组合越多。
下面这些是相机开发里最常见的配置点,每一项都对应一个适用场景:
| 配置项 | 常见默认 | 什么时候需要调整 |
|---|---|---|
| 权限声明 | Info.plist 必须包含 NSCameraUsageDescription | 缺少声明会导致启动后直接崩溃或权限弹窗不出现 |
| 输入设备 | builtInWideAngleCamera | 涉及变焦功能时要区分 ultraWide、telephoto 机型 |
| 照片格式 | photoOutput 默认格式 | 需要高分辨率或特定编码时,要确认机型支持 |
| 对焦模式 | continuousAutoFocus | 固定机位或需要锁定焦平面时改用 locked |
| 曝光模式 | continuousAutoExposure | 想避免亮度跳动时锁定曝光,或手动设置曝光补偿 |
| 白平衡 | continuousAutoWhiteBalance | 光线稳定时锁定,可以避免颜色在拍视频时跳变 |
| 闪光灯 | auto | 强逆光、暗光且主体较近时,auto 策略更可靠 |
| 连拍/定时 | 由用户触发 | 抓拍动态场景时,连拍是比单张更安全的兜底策略 |
这里最关键的一点是:不要替用户做决定,也不要假设所有机型行为一致。同一个配置在 iPhone 15 上正常,在更早机型上可能根本不支持。每次修改 session 配置时,都要检查返回值,并且用机型能力表做一层筛查。
3.3 预览黑屏、拍照无图时的排查顺序
在做相机项目时,你大概率会遇到“预览是黑的”“按了快门没有图”“有时能拍有时不能拍”这几种问题。这类问题很容易让人想改参数、换设备,但更高效的做法是按下面这个顺序排查:
- 先看权限状态:Info.plist 里声明了没有?用户是否拒绝过?权限状态是 authorized 吗?
- 再看 session 状态:session 有没有 running?配置有没有完成?有没有在 startRunning 之后立刻又 stopRunning?
- 再看预览层:AVCaptureVideoPreviewLayer 是否成功加入视图层级?frame 是否有效?session 是否被提前释放?
- 再看输入设备:设备是否存在?是不是在模拟器上调试?是不是被其他 App 占用?
- 再看输出回调:capturePhoto 有没有被调用?delegate 有没有被弱引用问题干掉?回调线程是否正确?
- 最后看数据本身:photoFileDataRepresentation 返回是否为空?保存的写入流程有没有报错?
这个顺序的核心逻辑是:从系统权限到 session 配置,再到 UI 展示,再到数据回调,一层一层缩小问题范围。大多数“预览黑屏”问题,最后都出在权限处理、session 生命周期或者模拟器和真机差异上。
4. 从能拍到能稳定生产,差的是工程化,不是一个功能
很多开发者做完一个相机 Demo 后,会误以为再写几个参数面板就能上线了。实际上,相机类 App 从“能跑”到“能稳定天天用”的距离,比普通应用要长得多。
4.1 相机 Session 是有生命周期的,不是打开就能一直拍
AVCaptureSession 不是一个创建之后就能永久运行的对象。它会在很多情况下被中断:
- 用户按下 Home 键或切到其他 App
- 来电、系统弹窗、控制中心呼出
- 前后摄像头切换
- 系统因为资源紧张或发热主动释放相机
所以一个合格的相机产品,必须监听 session 的中断和恢复事件,并在恢复后重新配置预览层。这还没完,你还要考虑用户从后台返回时,如果 session 已经 stopped,需要重新请求权限或重新启动。
这些逻辑单独看都不难,但它们彼此组合,就会形成很多状态分支。最容易出的 bug 是:用户切到后台再回来,画面卡在上一帧,或者直接黑屏。你需要在开发早期就设计好“中断—恢复”的状态机。
4.2 机型差异和热稳定性,会吃掉大部分调试时间
iPhone 机型之间的相机能力差异非常大。不是所有设备都有超广角,不是所有设备都支持同样的照片格式,低光场景下的表现也完全不同。
你的代码里一旦写了某个固定的 sessionPreset 或固定的 activeFormat,就要做好一个准备:在部分机型上它可能返回错误,导致黑屏或者拍照失败。更稳妥的做法是,启动时读取当前设备的支持范围,再在支持的范围内设置参数。
另外,长时间开启相机预览会带来明显的发热和帧率下降。这不是你写几行优化代码就能彻底解决的,因为它涉及系统整体的热管理策略。你需要在预览层、编码参数、掉帧策略上留出余地,并且要实际测试长时间运行场景,而不是只测开机拍照那几分钟。
4.3 用户信任,才是拍照类产品最贵的资源
拍照类 App 有一个特殊属性:用户把照片交给它,意味着默认它不会丢你的数据。一次保存失败、一次闪退导致照片消失,用户对你的耐心就归零了。
所以工程化上必须考虑这些:
- 拍照后写入相册的失败重试机制
- 前后台切换时,未完成写入任务的保护
- 崩溃日志和用户反馈通道
- 导出流程,确保用户能方便地把照片取回系统相册
这些能力不性感,但它们决定了产品能不能活过用户的前十次使用。第三方相机如果连系统相册都无法稳定集成,那它再“修复”交互也没有意义。
5. 重新看待“修复型”相机应用:怎么选、怎么用、怎么做
聊回 Camera Sun 以及类似项目:作为一个用户或开发者,你该怎么看待它们?
我的建议是,不要被“Fixing the iPhone's Camera App”这种标题带着走,而是要回到自己的真实需求。并不是每个人都有相同的相机使用痛点,也不是每个痛点都值得换一个工具来解决。
5.1 用四步法判断一个第三方相机适不适合你
我一般会用四个问题来快速判断:
- 你的痛点属于哪一类?是切换模式太慢、曝光调整不够直接、还是缺少参数信息?不同痛点对应的解决方案完全不同。
- 默认相机是否真的做不到?有些问题其实是“你不知道怎么做”,而不是“系统不能做”。比如网格线、水平仪、曝光锁定,这些原生默认其实都有,只是藏得深。
- 替代方案是否引入了新成本?第三方相机可能要重新学习操作、可能要多一个权限授权、可能照片导出流程更繁琐。这些成本要算进去。
- 你能接受它在系统升级后的适配延迟吗?第三方相机通常比系统相机的更新节奏慢。iOS 大版本更新后,某些 API 行为可能变化,App 可能需要几个月才能跟上。
5.2 适合场景与不适合场景
| 适合的人群/场景 | 不太适合的人群/场景 |
|---|---|
| 经常觉得原生相机操作层级太深 | 完全依赖苹果 iCloud 照片同步和实况照片 |
| 需要快速切换模式、快速调整曝光 | 只希望开箱即用,不愿学习新界面 |
| 对摄影有一定兴趣,想看到更多参数 | 对第三方权限、隐私风险敏感 |
| 愿意为某个特定场景多装一个工具 | 担心 App 停止维护、照片无法导出 |
| 想用镜头语言练习构图和曝光 | 只在意最终成片滤镜观感 |
如果你属于右边的场景,那系统相机也许才是最适合你的。这不是退步,而是工具选择的一部分。
5.3 对开发者和普通用户分别说一句实话
对开发者:如果你要做相机 App,不要只做 UI Demo。先做 100 张连拍、20 次前后台切换、10 分钟持续预览的压力测试,再考虑自己的产品是否真的可以发布。
对普通用户:第三方相机可以帮助你解决某个特定场景的问题,但你不必让它替代系统相机。把系统相机留给日常快速记录,把第三方相机留给需要特定控制的时刻,两者共存往往比“替代”更务实。
回到“修复”这两个字
Camera Sun 这个项目的出现,背后其实是一个更朴素的想法:系统相机不是不可更改的标准答案,它只是“当前最流行的默认方案”。在“默认方案”和“更符合自己习惯的方案”之间,存在一个巨大的产品空间。
真正值得敬佩的,不是谁做了一款功能最多的相机,而是谁愿意把按下快门之前那些步骤,认真再数一遍,并且试着把它变得更短。哪怕最后只节省了 0.5 秒,在摄影这件事上,0.5 秒可能就是那张照片的全部价值。