【问题标题】:How to make a draggable UIView snap to the corners over the screen?如何使可拖动的 UIView 捕捉到屏幕上的角落?
【发布时间】:2021-06-06 02:47:00
【问题描述】:

我有一个可拖动的 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';您的意思是改用这种类型的值吗?

【问题讨论】:

  • 这里有一篇好文章:medium.com/@nathangitter/… ... 向下滚动到标题 Interface #7: FaceTime PiP - 您可能会发现它显示了如何完全按照您的要求进行操作(并且在很好,流畅的方式)。
  • @DonMag 那篇文章绝对有帮助,但我的代码似乎有一些错误。我编辑了原始问题以反映我能够根据那篇文章所做的更改。
  • 您需要发布您的完整代码(参见minimal reproducible example)。没有办法从点点滴滴中知道。
  • @DonMag 知道了。我用代码编辑了原始帖子。谢谢!
  • 嗯,这仍然只是代码的 sn-ps... 你说你在 for position in crystalInfoContainer 收到错误 --- crystalInfoContainer 是什么?对于与velocity 相关的第二个错误,您在哪里有该代码?发布完整课程的代码,以免我们猜测。

标签: ios swift iphone xcode uiview


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-14
    • 1970-01-01
    • 2014-07-28
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多