首页 > 解决方案 > 如何使可拖动的 UIView 捕捉到屏幕上的角落?

问题描述

我有一个可拖动的 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?” 符合“顺序”;你的意思是解开可选的吗?

错误 2:实例成员 'velocity' 不能用于类型 'UIPanGestureRecognizer';你的意思是使用这种类型的值吗?

标签: iosswiftiphonexcodeuiview

解决方案


问题是您的 panView 方法是错误的。您需要打开手势识别器的状态——开始、更改或结束。仅在手势更改时平移。当手势结束时,只有这样,才能将视图动画到最近的角落。


推荐阅读