在 Swift 4 中用一根手指旋转 [英] Make rotation with one finger in Swift 4

查看:22
本文介绍了在 Swift 4 中用一根手指旋转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个 UIGestureRecognizer 来仅用一根手指旋转视图.

I created a UIGestureRecognizer to rotate a view with only one finger.

视图在开始时旋转,但一旦达到一定程度,旋转就会向另一个方向旋转.

The view rotate at the beginning but as soon as it reached a certain degree the rotation rotate in the other direction.

你能帮我写代码吗?

UIViewcontroller <- 这里一切正常

UIViewcontroller <- Everything is fine here

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.

    let wheel = TestWheelView()
    wheel.frame = CGRect.init(x: self.view.center.x - 120, y: self.view.center.y - 120, width: 240, height: 240)
    self.view.addSubview(wheel)
    wheel.addGestureRecognizer(TestRotateGestureRecognizer())
}

UIGestureRecognizer <- 问题出在这里

UIGestureRecognizer <- The problem is here

import UIKit

class TestRotateGestureRecognizer: UIGestureRecognizer {
    var previousPoint = CGPoint()
    var currentPoint = CGPoint()
    var startAngle = CGFloat()
    var currentAngle = CGFloat()
    var currentRotation = CGFloat()
    var totalRotation = CGFloat()

    func angleForPoint(_ point:CGPoint) -> CGFloat{
        var angle = atan2(point.y - (self.view?.center.y)!, point.x - (self.view?.center.x)!)

        return angle
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesBegan(touches, with: event)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesMoved(touches, with: event)

        if let firstTouch = touches.first {
            previousPoint = firstTouch.previousLocation(in: self.view)
            currentPoint = firstTouch.location(in: self.view)
            startAngle = angleForPoint(previousPoint)
            currentAngle = angleForPoint(currentPoint)

            currentRotation = currentAngle - startAngle
            totalRotation += currentRotation

            self.view?.transform = CGAffineTransform(rotationAngle: totalRotation)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesEnded(touches, with: event)
    }
}

推荐答案

这更像是一道数学题,而不是一道 Swift 题.关于数学部分,您需要检查计算旋转的方法的结果是否为负并返回绝对值,否则返回 360 减去角度:

It is more a math question than a Swift question. Regarding the math part you need to check if the result of your method that calculates the rotation is negative and return the absolute value, otherwise return 360 minus the angle:

 func angle(from location: CGPoint) -> CGFloat {
    let deltaY = location.y - view.center.y
    let deltaX = location.x - view.center.x
    let angle = atan2(deltaY, deltaX) * 180 / .pi
    return angle < 0 ? abs(angle) : 360 - angle
}

关于动画部分我建议使用CABasicAnimation,将isRemovedOnCompletion设置为false,将fillMode设置为kCAFillModeForwards,将timingFunction设置为linear.其他重要设置是 from 和 to ,您应该对它们使用相同的值,持续时间为 0:

Regarding the animation part I suggest using CABasicAnimation, setting isRemovedOnCompletion to false, fillMode to kCAFillModeForwards and timingFunction to linear. Other important setting is the from and to which you should use the same value for both of them with a duration of 0:

fileprivate let rotateAnimation = CABasicAnimation()
func rotate(to: CGFloat, duration: Double = 0) {
    rotateAnimation.fromValue = to
    rotateAnimation.toValue = to
    rotateAnimation.duration = duration
    rotateAnimation.repeatCount = 0
    rotateAnimation.isRemovedOnCompletion = false
    rotateAnimation.fillMode = kCAFillModeForwards
    rotateAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    imageView.layer.add(rotateAnimation, forKey: "transform.rotation.z")
}

要将度数转换为弧度,您可以使用此 回答:

To convert from degrees to radians you can use the extension from this answer:

extension FloatingPoint {
    var degreesToRadians: Self { return self * .pi / 180 }
    var radiansToDegrees: Self { return self * 180 / .pi }
}

要保持启动之间的轮换,您可以向 UserDefault 添加一个计算属性:

To preserve the rotation between launches you can add a computed property to UserDefault:

extension UserDefaults {
    /// rotation persistant computed property
    var rotation: CGFloat {
        get {
            return CGFloat(double(forKey: "rotation"))
        }
        set {
            set(Double(newValue), forKey: "rotation")
        }
    }
}

<小时>

关于手势识别器,您需要切换手势状态以检测它的开始、更改和结束并采取相应措施:


Regarding the gesture recognizer you need to switch the gesture state to detect the begin, change and end of it and act accordingly:

fileprivate var rotation: CGFloat = UserDefaults.standard.rotation
fileprivate var startRotationAngle: CGFloat = 0
@objc func pan(_ gesture: UIPanGestureRecognizer) {
    let location = gesture.location(in: view)
    let gestureRotation = CGFloat(angle(from: location)) - startRotationAngle
    switch gesture.state {
    case .began:
        // set the start angle of rotation 
        startRotationAngle = angle(from: location)
    case .changed:
        rotate(to: rotation - gestureRotation.degreesToRadians)
    case .ended:
        // update the amount of rotation
        rotation -= gestureRotation.degreesToRadians
    default :
        break
    }
    // save the final position of the rotation to defaults
    UserDefaults.standard.rotation = rotation
}

并将手势识别器添加到您的视图中:

And add the gesture recognizer to your view:

class ViewController: UIViewController, UIGestureRecognizerDelegate {
    @IBOutlet weak var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        let address = "https://i.stack.imgur.com/xnZXF.jpg"
        let url = URL(string: address)!
        rotate(to: rotation)
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else { return }
            DispatchQueue.main.async {
                self.imageView.image = UIImage(data: data)
                let pan = UIPanGestureRecognizer(target: self, action:#selector(self.pan))
                pan.minimumNumberOfTouches = 1
                pan.maximumNumberOfTouches = 1
                pan.delegate = self
                self.view.addGestureRecognizer(pan)
            }
        }.resume()
    }
    // rest of the view controller code
}

示例项目

这篇关于在 Swift 4 中用一根手指旋转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆