在 Swift 4 中用一根手指进行旋转



我创建了一个UIGestureRecognizer来旋转视图。

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

你能帮我写代码吗?

UIViewcontroller <- 这里一切都很好

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 <- 问题就在这里

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)
    }
}

这与其说是斯威夫特问题,不如说是数学问题。关于数学部分,您需要检查计算旋转的方法的结果是否为负并返回绝对值,否则返回 360 减去角度:

 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,将isRemovedOnComplete设置为false,fillMode设置为kCAFillModeForwards,将timeingFunction设置为线性。其他重要设置是 from 和 to,您应该对两者使用相同的值,持续时间为 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")
}

要将度数转换为弧度,您可以使用此答案中的扩展名:

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

若要保留启动之间的旋转,可以将计算属性添加到 UserDefault:

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

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

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
}

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

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
}

示例项目

最新更新