Я пытаюсь следовать этому руководству и настроить камеру на моем контроллере вида.
У меня такой же PreviewView.swift:
class PreviewView: UIView {
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
guard let layer = layer as? AVCaptureVideoPreviewLayer else {
fatalError("Expected `AVCaptureVideoPreviewLayer` type for layer. Check PreviewView.layerClass implementation.")
}
//layer.videoGravity = .resizeAspectFill
return layer
}
var session: AVCaptureSession? {
get {
return videoPreviewLayer.session
}
set {
videoPreviewLayer.session = newValue
//Should I add videoPreviewLayer to the PreviewView's sublayer here somehow?
}
}
// MARK: UIView
override class var layerClass: AnyClass {
return AVCaptureVideoPreviewLayer.self
}
}
Затем в CameraViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Camera"
guard AVCaptureDevice.default(for: AVMediaType.video) != nil else {
fatalError("No video device found")
}
// Disable UI. Enable the UI later, if and only if the session starts running.
recordButton.isEnabled = false
// Set up the video preview view.
previewView.session = session
/*
Check video authorization status. Video access is required and audio
access is optional. If the user denies audio access, AVCam won't
record audio during movie recording.
*/
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
// The user has previously granted access to the camera.
break
case .notDetermined:
/*
The user has not yet been presented with the option to grant
video access. We suspend the session queue to delay session
setup until the access request has completed.
Note that audio access will be implicitly requested when we
create an AVCaptureDeviceInput for audio during session setup.
*/
sessionQueue.suspend()
AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in
if !granted {
self.setupResult = .notAuthorized
}
self.sessionQueue.resume()
})
default:
// The user has previously denied access.
setupResult = .notAuthorized
}
/*
Setup the capture session.
In general, it is not safe to mutate an AVCaptureSession or any of its
inputs, outputs, or connections from multiple threads at the same time.
Don't perform these tasks on the main queue because
AVCaptureSession.startRunning() is a blocking call, which can
take a long time. We dispatch session setup to the sessionQueue, so
that the main queue isn't blocked, which keeps the UI responsive.
*/
sessionQueue.async {
self.configureSession()
}
}
private func configureSession() {
if setupResult != .success {
return
}
session.beginConfiguration()
/*
We do not create an AVCaptureMovieFileOutput when setting up the session because
Live Photo is not supported when AVCaptureMovieFileOutput is added to the session.
*/
session.sessionPreset = .high
// Add video input.
do {
var defaultVideoDevice: AVCaptureDevice?
// Choose the back dual camera if available, otherwise default to a wide angle camera.
if let dualCameraDevice = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back) {
defaultVideoDevice = dualCameraDevice
} else if let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
// If a rear dual camera is not available, default to the rear wide angle camera.
defaultVideoDevice = backCameraDevice
}
guard let videoDevice = defaultVideoDevice else {
print("Default video device is unavailable.")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice)
if session.canAddInput(videoDeviceInput) {
session.addInput(videoDeviceInput)
self.videoDeviceInput = videoDeviceInput
DispatchQueue.main.async {
/*
Dispatch video streaming to the main queue because AVCaptureVideoPreviewLayer is the backing layer for PreviewView.
You can manipulate UIView only on the main thread.
Note: As an exception to the above rule, it is not necessary to serialize video orientation changes
on the AVCaptureVideoPreviewLayer’s connection with other session manipulation.
Use the status bar orientation as the initial video orientation. Subsequent orientation changes are
handled by CameraViewController.viewWillTransition(to:with:).
*/
//
// self.videoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)
// self.videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
// self.videoPreviewLayer?.frame = self.view.layer.bounds
// self.previewView.layer.addSublayer(self.videoPreviewLayer!)
let statusBarOrientation = UIApplication.shared.statusBarOrientation
var initialVideoOrientation: AVCaptureVideoOrientation = .portrait
if statusBarOrientation != .unknown {
if let videoOrientation = AVCaptureVideoOrientation(interfaceOrientation: statusBarOrientation) {
initialVideoOrientation = videoOrientation
}
}
self.previewView.videoPreviewLayer.connection?.videoOrientation = initialVideoOrientation
}
} else {
print("Couldn't add video device input to the session.")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
} catch {
print("Couldn't create video device input: \(error)")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
// Add audio input.
do {
let audioDevice = AVCaptureDevice.default(for: .audio)
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!)
if session.canAddInput(audioDeviceInput) {
session.addInput(audioDeviceInput)
} else {
print("Could not add audio device input to the session")
}
} catch {
print("Could not create audio device input: \(error)")
}
session.commitConfiguration()
}
Так что у меня сложилось впечатление, что, поскольку сеанс настраивается на другойпоток, возможно, AVCaptureVideoPreviewLayer не добавляется как-то?Когда я пытаюсь добавить его непосредственно в PreviewView.swift, я получаю эту ошибку:
* Завершение приложения из-за необработанного исключения 'CALayerInvalid', причина: 'слой является частью цикла в его дереве слоев* Первый вызов стека вызовов:
Я не очень понимаю, как еще PreviewLayer добавляется к представлению в руководстве по apple, но похоже, что оно работает в этом приложении, поэтому я не уверен, что происходитна