我有一个简单的相机预览实现:
import SwiftUI
import AVFoundation
struct CameraView: View {
@StateObject var model = CameraModel()
var body: some View {
CameraPreview(camera: model)
.safeAreaInset(edge: .bottom, alignment: .center, spacing: 0) {
Color.clear
.frame(height: 0)
.background(Material.bar)
}
.ignoresSafeArea(.all, edges: .top)
.onAppear() {
model.check()
}
}
}
struct CameraPreview: UIViewRepresentable {
@ObservedObject var camera: CameraModel
func makeUIView(context: Context) -> some UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.videoGravity = AVLayerVideoGravity.resizeAspectFill
camera.preview.frame = view.frame
view.layer.addSublayer(camera.preview)
camera.start()
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
}
struct CameraView_Previews: PreviewProvider {
static var previews: some View {
CameraView()
}
}
class CameraModel: ObservableObject {
@Published var session = AVCaptureSession()
@Published var alert = false
@Published var preview: AVCaptureVideoPreviewLayer!
func check() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setUp()
break
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { (status) in
if status {
self.setUp()
}
}
break
case .denied:
self.alert.toggle()
break
default:
break
}
}
func setUp() {
do {
self.session.beginConfiguration()
let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back)
let input = try AVCaptureDeviceInput(device: device!)
if self.session.canAddInput(input) {
self.session.addInput(input)
}
self.session.commitConfiguration()
}
catch {
print(error.localizedDescription)
}
}
func start() {
self.session.startRunning()
}
}
问题是它不处理屏幕旋转:
我找到了类似的主题,例如 this one ,但我是iOS开发的菜鸟,我什至不明白该解决方案放在哪里。我检查过 View
和 UIViewRepresentable
都没有要重写的此类方法。
如何在AVCaptureVideoPreviewLayer
中处理屏幕旋转?
最佳答案
这是一个基于 Dscyre Scotti'es answer 的视频旋转工作变体:
struct CameraView: View {
@StateObject var model = CameraModel()
var body: some View {
CameraPreview(camera: model)
.safeAreaInset(edge: .bottom, alignment: .center, spacing: 0) {
Color.clear
.frame(height: 0)
.background(Material.bar)
}
.ignoresSafeArea(.all, edges: [.top, .horizontal])
.onAppear() {
model.check()
}
}
}
struct CameraPreview: UIViewRepresentable {
@ObservedObject var camera: CameraModel
class LayerView: UIView {
var parent: CameraPreview! = nil
override func layoutSubviews() {
super.layoutSubviews()
// To disable default animation of layer. You can comment out those lines with `CATransaction` if you want to include
CATransaction.begin()
CATransaction.setDisableActions(true)
layer.sublayers?.forEach({ layer in
layer.frame = UIScreen.main.bounds
})
self.parent.camera.rotate(orientation: UIDevice.current.orientation)
CATransaction.commit()
}
}
func makeUIView(context: Context) -> some UIView {
let view = LayerView()
view.parent = self
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.videoGravity = AVLayerVideoGravity.resizeAspectFill
camera.preview.frame = view.frame
view.layer.addSublayer(camera.preview)
camera.start()
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
}
struct CameraView_Previews: PreviewProvider {
static var previews: some View {
CameraView()
}
}
class CameraModel: ObservableObject {
@Published var session = AVCaptureSession()
@Published var alert = false
@Published var preview: AVCaptureVideoPreviewLayer!
func check() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setUp()
break
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { (status) in
if status {
self.setUp()
}
}
break
case .denied:
self.alert.toggle()
break
default:
break
}
}
func setUp() {
do {
self.session.beginConfiguration()
let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back)
let input = try AVCaptureDeviceInput(device: device!)
if self.session.canAddInput(input) {
self.session.addInput(input)
}
self.session.commitConfiguration()
}
catch {
print(error.localizedDescription)
}
}
func start() {
self.session.startRunning()
}
func rotate(orientation: UIDeviceOrientation) {
let videoConnection = self.preview.connection
switch orientation {
case .portraitUpsideDown:
videoConnection?.videoOrientation = .portraitUpsideDown
case .landscapeLeft:
videoConnection?.videoOrientation = .landscapeRight
case .landscapeRight:
videoConnection?.videoOrientation = .landscapeLeft
case .faceDown:
videoConnection?.videoOrientation = .portraitUpsideDown
default:
videoConnection?.videoOrientation = .portrait
}
}
}
关于ios - 如何处理 AVCaptureVideoPreviewLayer 的设备旋转?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71700250/