import SwiftUI import AVFoundation struct CustomCameraView: UIViewControllerRepresentable { @Binding var isPresented: Bool let onImageCaptured: (UIImage) -> Void @Environment(\.presentationMode) private var presentationMode func makeUIViewController(context: Context) -> CustomCameraViewController { let viewController = CustomCameraViewController() viewController.delegate = context.coordinator return viewController } func updateUIViewController(_ uiViewController: CustomCameraViewController, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, CustomCameraViewControllerDelegate { let parent: CustomCameraView init(_ parent: CustomCameraView) { self.parent = parent } func didCaptureImage(_ image: UIImage) { parent.onImageCaptured(image) parent.presentationMode.wrappedValue.dismiss() } func didCancel() { parent.presentationMode.wrappedValue.dismiss() } } } protocol CustomCameraViewControllerDelegate: AnyObject { func didCaptureImage(_ image: UIImage) func didCancel() } class CustomCameraViewController: UIViewController { private var captureSession: AVCaptureSession? private var photoOutput: AVCapturePhotoOutput? private var previewLayer: AVCaptureVideoPreviewLayer? private var captureDevice: AVCaptureDevice? weak var delegate: CustomCameraViewControllerDelegate? private lazy var captureButton: UIButton = { let button = UIButton(type: .system) button.backgroundColor = .white button.tintColor = .black button.layer.cornerRadius = 35 button.layer.borderWidth = 5 button.layer.borderColor = UIColor.lightGray.cgColor button.addTarget(self, action: #selector(capturePhoto), for: .touchUpInside) return button }() private lazy var closeButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(systemName: "xmark"), for: .normal) button.tintColor = .white button.addTarget(self, action: #selector(closeCamera), for: .touchUpInside) return button }() private lazy var flipButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(systemName: "arrow.triangle.2.circlepath.camera"), for: .normal) button.tintColor = .white button.addTarget(self, action: #selector(switchCamera), for: .touchUpInside) return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black checkCameraPermissions() } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 确保预览层填满整个视图 previewLayer?.frame = view.bounds // 更新视频方向 if let connection = previewLayer?.connection, connection.isVideoOrientationSupported { connection.videoOrientation = .portrait } } private func checkCameraPermissions() { switch AVCaptureDevice.authorizationStatus(for: .video) { case .authorized: setupCamera() case .notDetermined: AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in DispatchQueue.main.async { if granted { self?.setupCamera() } else { self?.delegate?.didCancel() } } } default: delegate?.didCancel() } } private func setupCamera() { let session = AVCaptureSession() session.sessionPreset = .high // 修改这里:默认使用后置摄像头 guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { delegate?.didCancel() return } captureDevice = device do { let input = try AVCaptureDeviceInput(device: device) if session.canAddInput(input) { session.addInput(input) } let output = AVCapturePhotoOutput() if session.canAddOutput(output) { session.addOutput(output) photoOutput = output } // 创建预览层并确保填满整个屏幕 let previewLayer = AVCaptureVideoPreviewLayer(session: session) previewLayer.videoGravity = .resizeAspectFill previewLayer.frame = view.bounds previewLayer.connection?.videoOrientation = .portrait // 确保预览层填满整个视图 view.layer.insertSublayer(previewLayer, at: 0) self.previewLayer = previewLayer DispatchQueue.global(qos: .userInitiated).async { session.startRunning() DispatchQueue.main.async { self.setupUI() } } captureSession = session } catch { print("Error setting up camera: \(error)") delegate?.didCancel() } } private func setupUI() { view.bringSubviewToFront(closeButton) view.bringSubviewToFront(flipButton) view.bringSubviewToFront(captureButton) view.addSubview(closeButton) closeButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ closeButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20), closeButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), closeButton.widthAnchor.constraint(equalToConstant: 44), closeButton.heightAnchor.constraint(equalToConstant: 44) ]) view.addSubview(flipButton) flipButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ flipButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20), flipButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), flipButton.widthAnchor.constraint(equalToConstant: 44), flipButton.heightAnchor.constraint(equalToConstant: 44) ]) view.addSubview(captureButton) captureButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ captureButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), captureButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -30), captureButton.widthAnchor.constraint(equalToConstant: 70), captureButton.heightAnchor.constraint(equalToConstant: 70) ]) } @objc private func capturePhoto() { let settings = AVCapturePhotoSettings() photoOutput?.capturePhoto(with: settings, delegate: self) } @objc private func closeCamera() { delegate?.didCancel() } @objc private func switchCamera() { guard let currentInput = captureSession?.inputs.first as? AVCaptureDeviceInput else { return } let newPosition: AVCaptureDevice.Position = currentInput.device.position == .front ? .back : .front guard let newDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: newPosition) else { return } do { let newInput = try AVCaptureDeviceInput(device: newDevice) captureSession?.beginConfiguration() captureSession?.removeInput(currentInput) if captureSession?.canAddInput(newInput) == true { captureSession?.addInput(newInput) captureDevice = newDevice } else { captureSession?.addInput(currentInput) } captureSession?.commitConfiguration() } catch { print("Error switching camera: \(error)") } } } extension CustomCameraViewController: AVCapturePhotoCaptureDelegate { func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { if let error = error { print("Error capturing photo: \(error)") return } guard let imageData = photo.fileDataRepresentation(), let image = UIImage(data: imageData) else { return } let fixedImage = image.fixedOrientation() DispatchQueue.main.async { self.delegate?.didCaptureImage(fixedImage) } } } extension UIImage { func fixedOrientation() -> UIImage { if imageOrientation == .up { return self } UIGraphicsBeginImageContextWithOptions(size, false, scale) draw(in: CGRect(origin: .zero, size: size)) let normalizedImage = UIGraphicsGetImageFromCurrentImageContext() ?? self UIGraphicsEndImageContext() return normalizedImage } }