Search code examples
iosswifttransformtrigonometrytouch-event

Calculate rotation velocity and naturally rotate view iOS


I have a circle-shaped view and have it rotate via the following code overriding touchesBegan(touches:, withEvent:) and touchesMoved(touches:, withEvent:).

var deltaAngle = CGFloat(0)
var startTransform: CGAffineTransform?

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
    let touchPoint = touches.first!.locationInView(view)

    let dx = touchPoint.x - view.center.x
    let dy = touchPoint.y - view.center.y

    deltaAngle = atan2(dy, dx)
    startTransform = arrowPickerView.transform
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
    let touchPoint = touches.first!.locationInView(view)

    let dx = touchPoint.x - view.center.x
    let dy = touchPoint.y - view.center.y
    let angle = atan2(dy, dx)
    let angleDifference = deltaAngle - angle
    let transform = CGAffineTransformRotate(startTransform!, -angleDifference)
    arrowPickerView.transform = transform
}

I want to override touchesEnded(touches:, withEvent:) to calculate the velocity and have the view naturally rotate a little (similar to continuous scrolling). I currently save the original transform and calculate the delta angle. How can I implement this? Thanks in advance!


Solution

  • In my example below, the CGAffineTransformRotate depends on:

    • direction (if the user moves from left to right, up or down etc)
    • rotation (a factor dependent on the time between touchesBegan and touchesEnded)
    • PI

    This creates an CGAffineTransformRotate and this rotates with duration 1 (s), to create a feel of kinetic scrolling the UIViewAnimationOptions.CurveEaseOut property is used

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    
        let touchPointEnd = touches.first!.locationInView(view)
    
        self.dateTouchesEnded = NSDate()
    
        let delay : NSTimeInterval = NSTimeInterval(0)
    
        let timeDelta : Double = self.dateTouchesEnded!.timeIntervalSince1970 - self.dateTouchesStarted!.timeIntervalSince1970
    
        let horizontalDistance : Double = Double(touchPointEnd.x - self.touchPointStart!.x)
        let verticalDistance : Double = Double(touchPointEnd.y - self.touchPointStart!.y)
    
        var direction : Double = 1
        if (fabs(horizontalDistance) > fabs(verticalDistance)) {
            if horizontalDistance > 0 {
                direction = -1
            }
        } else {
            if verticalDistance < 0 {
                direction = -1
            }
        }
    
        let rotation : Double = (0.1 / timeDelta < 0.99) ? 0.1 / timeDelta : 0.99
    
        let duration : NSTimeInterval = NSTimeInterval(1)
        UIView.animateWithDuration(duration, delay: delay, options: UIViewAnimationOptions.CurveEaseOut, animations: {
            let transform = CGAffineTransformRotate(self.imageView.transform, CGFloat(direction) * CGFloat(rotation) * CGFloat(M_PI))
            self.imageView.transform = transform
            }, completion: nil)
    
    }
    

    I added the variables to keep track of the start and end time of touches and added the CGPoint location of the touchesBegan function

    var dateTouchesStarted : NSDate?
    var dateTouchesEnded : NSDate?
    var touchPointStart : CGPoint?
    

    In touchesBegan i added:

    self.touchPointStart = touchPoint
    

    To set the variable as explained above.