0

目标

使用 MTKView,复制 AVCaptureVideoPreviewLayer 或 Apple 的相机应用程序的重力。视频设备方向不会改变。摄像头的边缘不会移动一个像素,从不露出屏幕背景。其他屏幕上的 VC 正常旋转。

观察到的

在 期间应用Tech QA 1890 的变换viewWillTransitionMTKView确实会反向旋转......但旋转仍然令人不舒服地可见。在动画过程中视图的边缘被取消固定,掩盖了一些相机像素并为持有 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
        }
    }
    
}
4

1 回答 1

0

我认为“最简单”的解决方案实际上是阻止您的 UI 旋转并观察设备方向更改以仅手动旋转您想要旋转的界面元素。

在您的视图控制器中,您可以覆盖shouldAutorotate以防止自动旋转并用于supportedInterfaceOrientations仅返回一个允许的方向。

可以使用它们的transform属性手动旋转 UI 元素。

于 2021-06-12T10:36:43.990 回答