You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift/Xcode实现拍照自动保存上传(取消确认重拍选项)

问题描述

我正在Xcode中用Swift开发一款个人自用APP,功能是调用相机拍照后通过HTTP把照片发送到自己搭建的服务器(无需考虑隐私问题)。目前用系统相机拍照后,用户必须选择「Use Photo」确认或者「Retake Photo」重拍,我想移除这个确认步骤,让拍摄的照片自动保存并发送,不需要用户手动选择。想知道用标准Camera API能不能实现?我了解didFinishPickingMediaWithInfo参数可能相关,是否需要借助第三方库?(代码大部分由ChatGPT生成,我还没完全理解)

现有代码
import SwiftUI

struct ContentView: View {
    @State private var isImagePickerPresented = false
    @State private var capturedImage: UIImage?
    @State private var isCameraAuthorized = false
    
    var body: some View {
        VStack {
            Button("take another photo") { // This is only if the user wants to take another photo
                isImagePickerPresented.toggle()
            }
            .sheet(isPresented: $isImagePickerPresented, onDismiss: {
                
            }) {
                ImagePicker(image: $capturedImage, onImageCapture: { imageData in
                    if let imageData = imageData {
                        uploadImage(imageData: imageData)
                    }
                })
            }
            .onAppear {
                // Display the camera immediately when the view appears
                isImagePickerPresented.toggle()
            }
            
            // Manually send the image to the server. The app should send it automatically after the user confirms.
            Button(action: {
                if let imageData = capturedImage?.jpegData(compressionQuality: 0.5) {
                    uploadImage(imageData: imageData)
                }
            }) {
                VStack {
                    Text("Cool")
                        .font(.system(size: 70))
                        .padding(69)
                        .overlay(
                            RoundedRectangle(cornerRadius: 15)
                                .stroke(Color.black, lineWidth: 8)
                        )
                }
                .contentShape(Rectangle())
            }
            .background(Color.red)
            .foregroundColor(.white)
            .cornerRadius(15)
        }
        .background(
            Image("gigachad") // This is just the image for the background of the app
                .scaledToFit()
                .edgesIgnoringSafeArea(.all)
        )
    }
}

@main
struct BacknForthApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

// ImagePicker.swift
import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var image: UIImage?
    var onImageCapture: ((Data?) -> Void)
    @Environment(.presentationMode) var presentationMode

    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }

    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = .camera
        picker.allowsEditing = false

        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        var parent: ImagePicker

        init(parent: ImagePicker) {
            self.parent = parent
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage

                let imageData = uiImage.jpegData(compressionQuality: 0.5)
                parent.onImageCapture(imageData)

                if let imageData = imageData {
                    parent.onImageCapture(imageData)
                }
            }

            parent.presentationMode.wrappedValue.dismiss()  // Dismiss the ImagePicker
        }
    }
}
解决方案

核心结论

UIImagePickerController是系统封装好的相机界面,无法移除默认的「Use/Retake」确认步骤。要实现拍照后自动获取照片并上传的需求,需要使用更底层的系统框架AVFoundation,完全不需要依赖第三方库。

具体实现步骤

1. 实现AVFoundation相机视图

创建一个基于UIViewControllerRepresentable的相机视图,直接控制相机捕获流程,跳过确认环节:

import SwiftUI
import AVFoundation

struct CameraView: UIViewControllerRepresentable {
    var onImageCaptured: (UIImage) -> Void
    
    func makeUIViewController(context: Context) -> CameraViewController {
        let vc = CameraViewController()
        vc.onImageCaptured = onImageCaptured
        return vc
    }
    
    func updateUIViewController(_ uiViewController: CameraViewController, context: Context) {}
}

class CameraViewController: UIViewController, AVCapturePhotoCaptureDelegate {
    private var captureSession: AVCaptureSession!
    private var previewLayer: AVCaptureVideoPreviewLayer!
    var onImageCaptured: ((UIImage) -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCaptureSession()
        setupPreviewLayer()
        startSession()
        
        // 相机初始化完成后自动拍照(可根据需求调整延迟时间)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.capturePhoto()
        }
    }
    
    // 配置相机捕获会话
    private func setupCaptureSession() {
        captureSession = AVCaptureSession()
        captureSession.sessionPreset = .photo
        
        guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
            print("无法获取后置摄像头")
            return
        }
        
        do {
            let input = try AVCaptureDeviceInput(device: backCamera)
            if captureSession.canAddInput(input) {
                captureSession.addInput(input)
            }
            
            let photoOutput = AVCapturePhotoOutput()
            if captureSession.canAddOutput(photoOutput) {
                captureSession.addOutput(photoOutput)
                photoOutput.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
            }
        } catch {
            print("摄像头初始化失败: \(error.localizedDescription)")
        }
    }
    
    // 设置相机预览层
    private func setupPreviewLayer() {
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)
    }
    
    // 启动捕获会话
    private func startSession() {
        DispatchQueue.global(qos: .userInitiated).async {
            self.captureSession.startRunning()
        }
    }
    
    // 触发拍照
    private func capturePhoto() {
        guard let photoOutput = captureSession.outputs.first as? AVCapturePhotoOutput else { return }
        let settings = AVCapturePhotoSettings()
        photoOutput.capturePhoto(with: settings, delegate: self)
    }
    
    // 照片捕获完成回调
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        if let error = error {
            print("拍照失败: \(error.localizedDescription)")
            return
        }
        
        guard let imageData = photo.fileDataRepresentation(), let image = UIImage(data: imageData) else {
            print("无法处理照片数据")
            return
        }
        
        // 传递捕获到的照片并自动上传
        onImageCaptured?(image)
        // 拍照完成后关闭相机界面
        dismiss(animated: true)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if captureSession.isRunning {
            captureSession.stopRunning()
        }
    }
}

2. 修改ContentView替换相机组件

把原来的ImagePicker替换为自定义的CameraView,实现自动拍照上传:

struct ContentView: View {
    @State private var isCameraPresented = false
    @State private var capturedImage: UIImage?
    
    var body: some View {
        VStack {
            Button("重新拍照") {
                isCameraPresented.toggle()
            }
            .sheet(isPresented: $isCameraPresented) {
                CameraView { image in
                    self.capturedImage = image
                    // 自动执行上传逻辑
                    if let imageData = image.jpegData(compressionQuality: 0.5) {
                        uploadImage(imageData: imageData)
                    }
                }
            }
            .onAppear {
                // APP启动后直接打开相机
                isCameraPresented.toggle()
            }
            
            // 保留手动上传按钮(可选,根据需求决定是否保留)
            Button(action: {
                if let imageData = capturedImage?.jpegData(compressionQuality: 0.5) {
                    uploadImage(imageData: imageData)
                }
            }) {
                VStack {
                    Text("Cool")
                        .font(.system(size: 70))
                        .padding(69)
                        .overlay(
                            RoundedRectangle(cornerRadius: 15)
                                .stroke(Color.black, lineWidth: 8)
                        )
                }
                .contentShape(Rectangle())
            }
            .background(Color.red)
            .foregroundColor(.white)
            .cornerRadius(15)
        }
        .background(
            Image("gigachad")
                .scaledToFit()
                .edgesIgnoringSafeArea(.all)
        )
    }
    
    // 你的照片上传方法(请补充具体HTTP逻辑)
    func uploadImage(imageData: Data) {
        print("开始上传照片")
        // 示例:这里添加你的POST请求代码,把imageData发送到服务器
    }
}

@main
struct BacknForthApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

3. 配置相机权限

在Info.plist中添加相机权限描述,否则APP会崩溃:

<key>NSCameraUsageDescription</key>
<string>需要访问相机进行拍照</string>

关键说明

  • 自动拍照时机:示例中添加了0.5秒延迟,是为了确保相机完成初始化,你可以根据实际体验调整延迟时间,或者改为手动触发拍照(比如添加一个拍照按钮)。
  • 完全基于系统API:整个实现只用到了AVFoundation和SwiftUI的系统框架,不需要任何第三方库。
  • 流程简化:拍照完成后直接回调获取照片,跳过了系统相机的确认环节,直接执行上传逻辑。

内容的提问来源于stack exchange,提问作者Kompyuter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 16:20:58