我正在使用 this 相机库,其中作者将
AVCaptureVideoPreviewLayer
包装在 UIViewControllerRepresentable
(link)内,以将其用作相机预览。在 updateUIViewController
内部,作者使用 frame
对 DispatchQueue.main.async
更新进行排队:
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
previewLayer.videoGravity = gravity
if previewLayer.superlayer == nil {
uiViewController.view.layer.addSublayer(previewLayer)
}
// print(uiViewController.view.bounds) -- point A
DispatchQueue.main.async {
// print(uiViewController.view.bounds) -- point B
self.previewLayer.frame = uiViewController.view.bounds
}
}
point A
和point B
之间可能存在差异,这是有道理的,因为使用DispatchQueue.main.async
不能保证排序,但我并不完全相信这种方法是正确的。我希望能够使用 frame
之类的东西设置此视图的 Preview().frame(width: 350, height: 500)
。然而,这里缺乏排序意味着根据应用程序/视图的其他部分,view.frame
更新会按照未知的时间表进行。例如以下内容:
import SwiftUI
import Aespa
@Observable
class ViewModel {
@ObservationIgnored var aespaSession = Aespa.session(with: AespaOption(albumName: nil))
@ObservationIgnored var preview: InteractivePreview {
aespaSession.interactivePreview(
gravity: .resizeAspectFill,
option: InteractivePreviewOption()
)
}
var show = false
}
struct ContentView: View {
@State var viewModel = ViewModel()
var body: some View {
ZStack {
VStack {
Spacer()
Button("Show Camera Preview") {
viewModel.show.toggle()
}
}
.ignoresSafeArea()
.zIndex(0)
if viewModel.show {
VStack {
viewModel.preview
.frame(width: 350, height: 500)
Spacer()
}
.zIndex(1)
}
}
}
}
打印:
Point A: (0.0, 0.0, 393.0, 852.0)
Point B: (0.0, 0.0, 350.0, 500.0)
但是,如果您将代码更改为其他内容,也许是
.zIndex(0)
处的不同背景(我不会在这里显示确切的代码,因为我的应用程序并不简单),它可能会打印:
Point A: (0.0, 0.0, 393.0, 852.0)
Point B: (0.0, 0.0, 393.0, 852.0)
所以我的问题是。
view.frame
的正确设置方法是什么? updateUIViewController
显然是按 some 确定性顺序调用的,但在 bound
“解决”之前,不是。我没有看到神奇的“updateViewOneLastTime”方法?
viewDidLayoutSubviews
,每当视图控制器的 view.bounds
发生变化时就会调用它。创建一个 UIViewController
子类并覆盖它。
class PreviewViewController: UIViewController {
var previewLayer: AVCaptureVideoPreviewLayer?
override func viewDidLayoutSubviews() {
previewLayer?.frame = view.frame
}
}
然后更改
UIViewControllerRepresentable
实现中的方法以使用该子类。在适当的情况下将预览层传递给视图控制器子类。
func makeUIViewController(context: Context) -> PreviewViewController {
let viewController = PreviewViewController()
viewController.view.backgroundColor = .clear
uiViewController.previewLayer = previewLayer
return viewController
}
func updateUIViewController(_ uiViewController: PreviewViewController, context: Context) {
previewLayer.videoGravity = gravity
if previewLayer.superlayer == nil {
uiViewController.view.layer.addSublayer(previewLayer)
}
uiViewController.previewLayer = previewLayer
}
func dismantleUIViewController(_ uiViewController: PreviewViewController, coordinator: ()) {
previewLayer.removeFromSuperlayer()
}