目标
使用 MTKView,复制 AVCaptureVideoPreviewLayer 或 Apple 的相机应用程序的重力。视频设备方向不会改变。摄像头的边缘不会移动一个像素,从不露出屏幕背景。其他屏幕上的 VC 正常旋转。
观察到的
在 期间应用Tech QA 1890 的变换viewWillTransition
,MTKView
确实会反向旋转......但旋转仍然令人不舒服地可见。在动画过程中视图的边缘被取消固定,掩盖了一些相机像素并为持有 MTKView 的 VC 显示了一个白色背景。
问题
我怎样才能让这些边缘像受惊的蛤蜊一样粘在屏幕边界上?
我认为我的错误是在约束中,但我愿意在其他方面犯错。:)
查看层次结构
一个微型相机过滤器应用程序在固定到屏幕边缘的 MTKView(在 VC #2 中)上覆盖了相机控件(VC #1)。
UINavigationController
│
└─ CameraScreenVC
│
├── CameraControlsVC <- Please rotate subviews
│
└── MetalCameraFeedVC
└── MTKView <- Please no rotation edges
代码
下面的相关片段。
MetalCameraVC.swift
final class MetalCameraVC: UIViewController {
let mtkView = MTKView() // This VC's only view
/// Called in viewDidAppear
func setupMetal(){
metalDevice = MTLCreateSystemDefaultDevice()
mtkView.device = metalDevice
mtkView.isPaused = true
mtkView.enableSetNeedsDisplay = false
metalCommandQueue = metalDevice.makeCommandQueue()
mtkView.delegate = self
mtkView.framebufferOnly = false
ciContext = CIContext(
mtlDevice: metalDevice,
options: [.workingColorSpace: CGColorSpace(name: CGColorSpace.sRGB)!])
}
...
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
// blank
}
func draw(in mtkview: MTKView) {
image = image.transformed(by: scaleToScreenBounds)
image = image.cropped(to: mtkview.drawableSize.zeroOriginRect())
guard let buffer = metalCommandQueue.makeCommandBuffer(),
let currentDrawable = mtkview.currentDrawable
else { return }
ciContext.render(image,
to: currentDrawable.texture,
commandBuffer: buffer,
bounds: mtkview.drawableSize.zeroOriginRect(),
colorSpace: CGColorSpaceCreateDeviceRGB())
buffer.present(currentDrawable)
buffer.commit()
}
}
extension MetalCameraVC {
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mtkView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
mtkView.frame = view.frame
if let orientation = AVCaptureVideoOrientation.fromCurrentDeviceOrientation() {
lastOrientation = orientation
}
}
}
+旋转
/// Apple Technical QA 1890 Prevent View From Rotating
extension MetalCameraVC {
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
mtkView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate { [self] context in
let delta = coordinator.targetTransform
let deltaAngle = atan2(delta.b, delta.a)
var currentAngle = mtkView.layer.value(forKeyPath: "transform.rotation.z") as? CGFloat ?? 0
currentAngle += -1 * deltaAngle + 0.1
mtkView.layer.setValue(currentAngle, forKeyPath: "transform.rotation.z")
} completion: { [self] context in
var rounded = mtkView.transform
rounded.a = round(rounded.a)
rounded.b = round(rounded.b)
rounded.c = round(rounded.c)
rounded.d = round(rounded.d)
mtkView.transform = rounded
}
}
}