显示另一个 swiftUI 视图时如何停止相机工作?
Posted
技术标签:
【中文标题】显示另一个 swiftUI 视图时如何停止相机工作?【英文标题】:How to stop camera working when displaying another swiftUI view? 【发布时间】:2021-07-19 12:56:10 【问题描述】:我有一个超级简单的应用程序,它可以拍照并将其保存到 Apple 照片库,并有一个设置按钮,可以设置视图。
所以,我的问题是当我在 fullScreenCover 中打开设置时,相机仍在工作(绿色隐私指示灯没有消失),如果您有优雅的解决方案,请告诉我,将不胜感激。
代码如下:
import SwiftUI
struct ContentView: View
let cameraController: CustomCameraController
@ObservedObject var cameraViewModel: CameraViewModel
var body: some View
CameraView(cameraController: cameraController, cameraViewModel: cameraViewModel)
struct CameraView: View
@State private var didTapCapture = false
@State private var didTapSettings = false
let cameraController: CustomCameraController
@ObservedObject var cameraViewModel: CameraViewModel
var body: some View
VStack
ZStack
CameraPreviewRepresentable(didTapCapture: $didTapCapture, cameraViewModel: cameraViewModel, cameraController: cameraController)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.edgesIgnoringSafeArea(.all)
Spacer()
HStack(alignment: .center)
SettingsButton(didTap: $didTapSettings)
.frame(width: 80, height: 80, alignment: .leading)
Spacer()
.overlay(
CaptureButton(didTapCapture: $didTapCapture)
.frame(width: 100, height: 100, alignment: .center)
.padding(.bottom, 20)
)
.fullScreenCover(isPresented: $didTapSettings)
VStack(spacing: 30)
Text("Settings View ⚙️")
.font(.largeTitle)
Button("OK")
didTapSettings.toggle()
struct CaptureButton: View
@Binding var didTapCapture: Bool
var body: some View
Button
didTapCapture.toggle()
label:
Image(systemName: "camera")
.font(.largeTitle)
.padding(30)
.background(Color.red)
.foregroundColor(.white)
.clipShape(Circle())
.overlay(
Circle()
.stroke(Color.red)
)
struct SettingsButton: View
@Binding var didTap: Bool
var body: some View
Button
didTap.toggle()
label:
Image(systemName: "gearshape.2")
.font(.largeTitle)
.padding(30)
.foregroundColor(.white)
import SwiftUI
import Combine
import AVFoundation
struct CameraPreviewRepresentable: UIViewControllerRepresentable
@Environment(\.presentationMode) var presentationMode
@Binding var didTapCapture: Bool
@ObservedObject var cameraViewModel: CameraViewModel
let cameraController: CustomCameraController
func makeUIViewController(context: Context) -> CustomCameraController
cameraController.delegate = context.coordinator
return cameraController
func updateUIViewController(_ cameraViewController: CustomCameraController, context: Context)
if didTapCapture
cameraViewController.didTapRecord()
func makeCoordinator() -> Coordinator
Coordinator(self, cameraViewModel: cameraViewModel)
class Coordinator: NSObject, UINavigationControllerDelegate, AVCapturePhotoCaptureDelegate
let parent: CameraPreviewRepresentable
var cameraViewModel: CameraViewModel
var tokens = Set<AnyCancellable>()
init(_ parent: CameraPreviewRepresentable, cameraViewModel: CameraViewModel)
self.parent = parent
self.cameraViewModel = cameraViewModel
super.init()
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?)
parent.didTapCapture = false
if let imageData = photo.fileDataRepresentation(), let image = UIImage(data: imageData)
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
parent.presentationMode.wrappedValue.dismiss()
import Combine
import AVFoundation
class CameraViewModel: ObservableObject
@Published var exposureTargetOffset: Float = 0
import UIKit
import Combine
import AVFoundation
class CustomCameraController: UIViewController
var image: UIImage?
var captureSession = AVCaptureSession()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
lazy var currentCamera: AnyPublisher<AVCaptureDevice?, Never> = currentCameraSubject.eraseToAnyPublisher()
var photoOutput: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
private var currentCameraSubject = CurrentValueSubject<AVCaptureDevice?, Never>(nil)
//DELEGATE
var delegate: AVCapturePhotoCaptureDelegate?
func didTapRecord()
let settings = AVCapturePhotoSettings()
photoOutput?.capturePhoto(with: settings, delegate: delegate!)
override func viewDidLoad()
super.viewDidLoad()
setup()
func setup()
setupCaptureSession()
setupDevice()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
func setupCaptureSession()
captureSession.sessionPreset = .photo
func setupDevice()
let deviceDiscoverySession =
AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera],
mediaType: .video,
position: .unspecified)
for device in deviceDiscoverySession.devices
switch device.position
case .front:
self.frontCamera = device
case .back:
self.backCamera = device
default:
break
self.currentCameraSubject.send(self.backCamera)
func setupInputOutput()
do
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCameraSubject.value!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
captureSession.addOutput(photoOutput!)
catch
print(error)
func setupPreviewLayer()
self.cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
let deviceOrientation = UIDevice.current.orientation
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation(rawValue: deviceOrientation.rawValue)!
self.cameraPreviewLayer?.frame = self.view.frame
self.view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
func startRunningCaptureSession()
captureSession.startRunning()
【问题讨论】:
【参考方案1】:添加一种额外的停止方法captureSession
。
class CustomCameraController: UIViewController
/// Other code
func startRunningCaptureSession()
captureSession.startRunning()
func stopCaptureSession() // <<== Here
if captureSession.isRunning
captureSession.stopRunning()
现在,为启停会话再添加一个状态变量。 工作表上的更新状态变量出现 - 消失的方法。
struct CameraView: View
@State private var didTapCapture = false
@State private var didTapSettings = false
let cameraController: CustomCameraController
@ObservedObject var cameraViewModel: CameraViewModel
@State private var isRunning = true // << Here
var body: some View
VStack
ZStack
CameraPreviewRepresentable(isRunning: $isRunning, didTapCapture: $didTapCapture, cameraViewModel: cameraViewModel, cameraController: cameraController) // << Here
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.edgesIgnoringSafeArea(.all)
Spacer()
HStack(alignment: .center)
SettingsButton(didTap: $didTapSettings)
.frame(width: 80, height: 80, alignment: .leading)
Spacer()
.overlay(
CaptureButton(didTapCapture: $didTapCapture)
.frame(width: 100, height: 100, alignment: .center)
.padding(.bottom, 20)
)
.fullScreenCover(isPresented: $didTapSettings)
VStack(spacing: 30)
Text("Settings View ⚙️")
.font(.largeTitle)
Button("OK")
didTapSettings.toggle()
.onDisappear() // << Here
isRunning = true
.onAppear() // << Here
isRunning = false
绑定状态变量。
struct CameraPreviewRepresentable: UIViewControllerRepresentable
@Binding var isRunning: Bool // <--- Here
/// Other code
func updateUIViewController(_ cameraViewController: CustomCameraController, context: Context)
if isRunning // <--- Here
cameraController.startRunningCaptureSession()
if didTapCapture
cameraViewController.didTapRecord()
else
cameraController.stopCaptureSession()
/// Other code
【讨论】:
非常感谢 Raya ? 但我们有一个小问题,当我们关闭设置视图时相机开始冻结一两秒以上是关于显示另一个 swiftUI 视图时如何停止相机工作?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 检测视图何时不可见(有点视图会消失)并停止发布者