如何使可拖动的 UIView 捕捉到屏幕上的角落?

How to make a draggable UIView snap to the corners over the screen?

提问人:bigdogg99juherd 提问时间:3/8/2021 最后编辑:bigdogg99juherd 更新时间:3/10/2021 访问量:291

问:

我有一个可拖动的 UIView,我正在尝试让它捕捉到屏幕的四个角。我尝试了一些方法,但没有一个奏效。这是我拥有的代码:

import UIKit
import AVKit
import Vision

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {

    @IBOutlet weak var crystalName: UILabel!
    @IBOutlet weak var crystalInfoContainer: UIView!
    @IBOutlet weak var accuracy: UILabel!
    
    var model = IdenticrystClassification().model
    
    override func viewDidLoad() {
        super.viewDidLoad()
      
        // This method starts the camera.
       let captureSession = AVCaptureSession()
        
        guard let captureDevice = AVCaptureDevice.default(for: .video) else { return }
        guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return }
        captureSession.addInput(input)
        
        captureSession.startRunning()
        
        let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)
        previewLayer.frame = view.frame
        
        
        // This method defines sub view and defines it's properties.
        view.addSubview(crystalInfoContainer)
        crystalInfoContainer.clipsToBounds = true
        crystalInfoContainer.layer.cornerRadius = 10.0
        //crystalInfoContainer.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
        
      
        // This method defines torch functionality.
        func toggleTorch(on: Bool) {
            guard let device = AVCaptureDevice.default(for: .video) else { return }
            
            if device.hasTorch {
                do {
                    try device.lockForConfiguration()
                    
                    if on == true {
                        device.torchMode = .on
                    } else {
                        device.torchMode = .off
                    }
                    
                    device.unlockForConfiguration()
                } catch {
                    print("Torch could not be used")
                }
                } else {
                    print("Torch is not available")
                }
            }
        
        // This is the code that I am trying to work out.
      func relativeVelocity(forVelocity velocity: CGFloat, from currentValue: CGFloat, to targetValue:  CGFloat) -> CGFloat {
            guard currentValue - targetValue != 0 else { return 0 }
            return velocity / (targetValue - currentValue)
        }
        
        func nearestCorner(to point: CGPoint) -> CGPoint {
            var minDistance = CGFloat.greatestFiniteMagnitude
            var closestPosition = CGPoint.zero
            for position in crystalInfoContainer { **Error1**
                let distance = point.distance(to: position)
                if distance < minDistance {
                    closestPosition = position
                    minDistance = distance
                }
            }
            return closestPosition
        
        
        let decelerationRate = UIScrollView.DecelerationRate.normal.rawValue
        let velocity = UIPanGestureRecognizer.velocity(in: view)**Error2**
        let projectedPosition = CGPoint(
            x: crystalInfoContainer.center.x + project(initialVelocity: velocity.x, decelerationRate: decelerationRate),
            y: crystalInfoContainer.center.y + project(initialVelocity: velocity.y, decelerationRate: decelerationRate)
        )
       
        let nearestCornerPosition = nearestCorner(to: projectedPosition)
        
        let relativeInitialVelocity = CGVector(
            dx: relativeVelocity(forVelocity: velocity.x, from: crystalInfoContainer.center.x, to: nearestCornerPosition.x),
            dy: relativeVelocity(forVelocity: velocity.y, from: crystalInfoContainer.center.y, to: nearestCornerPosition.y)
        )
        
        let params = UISpringTimingParameters(damping: 1, response: 0.4, initialVelocity: relativeInitialVelocity)
        let animator = UIViewPropertyAnimator(duration: 0, timingParameters: params)
        animator.addAnimations {
            self.crystalInfoContainer.center = nearestCornerPosition
        }
        animator.startAnimation()
        
        }

        let dataOutput = AVCaptureVideoDataOutput()
                dataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "video"))
                captureSession.addOutput(dataOutput)
        toggleTorch(on: true)
        
    }

    // Handles Visiout output.
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        
        guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
        
        guard let model = try? VNCoreMLModel(for: model) else { return }
        let request = VNCoreMLRequest(model: model)
        { (finishedReq, err) in
            
         guard let results = finishedReq.results as? [VNClassificationObservation] else { return }
         guard let firstObservation = results.first else { return }
            let name: String = firstObservation.identifier
            let acc: Int = Int(firstObservation.confidence * 100)
            
            DispatchQueue.main.async {
                self.crystalName.text = name
                self.accuracy.text = "Confidence: \(acc)%"
                
            }
        }
    
        try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])

    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
}

错误 1:for-in 循环需要 'UIView?' 符合 'Sequence';你是想解开可选的吗?

错误 2:实例成员“velocity”不能用于类型“UIPanGestureRecognizer”;您是否打算改用此类型的值?

iOS Swift iPhone Xcode UIViview

评论

0赞 DonMag 3/8/2021
这里有一篇好文章:medium.com/@nathangitter/......向下滚动到标题 - 你可能会发现它显示了如何准确地按照你的要求去做(并且以一种漂亮、流畅的方式做到这一点)。Interface #7: FaceTime PiP
0赞 bigdogg99juherd 3/9/2021
@DonMag那篇文章绝对有帮助,但我的代码中似乎存在一些错误。我编辑了原始问题,以反映我能够根据该文章进行的更改。
0赞 DonMag 3/9/2021
您需要发布完整的代码(请参阅最小可重现示例)。没有办法从点点滴滴中知道。
0赞 bigdogg99juherd 3/10/2021
@DonMag知道了。我用代码编辑了原始帖子。谢谢!
0赞 DonMag 3/10/2021
好吧,这仍然只是代码片段......你说你在--- What is ?对于您的第二个错误,与 有关,您在哪里有该代码?发布完整课程的代码,这样我们就不会猜测了。for position in crystalInfoContainercrystalInfoContainervelocity

答:

0赞 matt 3/8/2021 #1

问题是你的panView方法是错误的。您需要打开手势识别器的状态 - 开始、更改或结束。仅当手势更改时才平移。当手势结束时,然后且仅如此,将视图动画化到最近的角落。