Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: How to make smooth inertia after panning(dragging)?

Tags:

ios

swift

swift2

I have been learning Swift 2.2 for several days. After finishing the official tutorial, I'm trying to make some functional samples. E.g. I am trying to make an avatar decorator.

@IBAction func moveAvatar(recognizer:UIPanGestureRecognizer) {
    recognizer.maximumNumberOfTouches = 1
    recognizer.minimumNumberOfTouches = 1
    let debugOutOfBounds = false
    let debugGestureRecognizer = false
    let debugVelocity = true
    if (debugGestureRecognizer) {
        print("pan")
    }
    let translation = recognizer.translationInView(self.avatar)

    let standardAvatarSize:CGFloat = Conf.Size.avatarRadius * 2
    if (self.avatar.frame.width <  standardAvatarSize) {
        self.avatar.frame = CGRect(origin: self.avatar.frame.origin, size: CGSize(width: standardAvatarSize, height:standardAvatarSize))
    }
    if let view = recognizer.view {
        switch recognizer.state {
        case .Began:
            if (debugVelocity) {
                print("Began: velocity: \(recognizer.velocityInView(view))  translation: \(translation)")
            }
            
            self.panAvatarLastTranslation = translation
            self.avatar.center = CGPoint(x: self.avatar.center.x + translation.x, y: self.avatar.center.y + translation.y)
        case .Changed:
            if (debugVelocity) {
                print("Changed: velocity: \(recognizer.velocityInView(view))  translation: \(translation)")
            }
            if (translation != CGPoint(x: 0.0, y: 0.0)) {
                self.panAvatarLastTranslation = translation
            }
            self.avatar.center = CGPoint(x: self.avatar.center.x + translation.x, y: self.avatar.center.y + translation.y)
        case .Ended:
            if (debugVelocity) {
                print("Ended: velocity: \(recognizer.velocityInView(view))  translation: \(translation)")
            }
            let velocityDistance = sqrt(pow(recognizer.velocityInView(view).x, CGFloat(2)) + pow(recognizer.velocityInView(view).y, CGFloat(2)))
            print(velocityDistance)
            if (velocityDistance > 100) {
                let inertiaDelay: NSTimeInterval = 0.5
                let inertiaDistance : CGFloat = velocityDistance * CGFloat(inertiaDelay) * 0.02
                let inertiaX:CGFloat = self.panAvatarLastTranslation.x * inertiaDistance
                let inertiaY:CGFloat = self.panAvatarLastTranslation.y * inertiaDistance
                                    //sqrt(pow(inertiaX, CGFloat(2)) + pow(inertiaY, CGFloat(2)))
                //print("inertiaDelay: \(inertiaDelay)")
                UIView.animateWithDuration(inertiaDelay, animations: {
                    self.avatar.center.x += inertiaX
                    self.avatar.center.y += inertiaY
                })
            }
        default:
            print("default")
        }

        let standardAvatarBound: [String:CGFloat] = [
            "top": UIApplication.sharedApplication().statusBarFrame.height + UIScreen.mainScreen().bounds.width / 2 - Conf.Size.avatarRadius,
            "right": UIScreen.mainScreen().bounds.width / 2 + Conf.Size.avatarRadius,
            "bottom": UIApplication.sharedApplication().statusBarFrame.height + UIScreen.mainScreen().bounds.width / 2 + Conf.Size.avatarRadius,
            "left": UIScreen.mainScreen().bounds.width / 2 - Conf.Size.avatarRadius
        ]
        let avatarBound: [String:CGFloat] = [
            "top": self.avatar.center.y - self.avatar.frame.height / 2,
            "right": self.avatar.frame.width / 2 + self.avatar.center.x,
            "bottom": self.avatar.center.y + self.avatar.frame.height / 2,
            "left": self.avatar.center.x - self.avatar.frame.width / 2
        ]
        
        if (self.panAvatarLastTranslation.x < 0 && avatarBound["right"] < standardAvatarBound["right"]) {
            UIView.animateWithDuration(0.5, animations: {
                self.avatar.center.x += (standardAvatarBound["right"]! - avatarBound["right"]!)
            })
            if (debugOutOfBounds) {
                print("Out of right bound")
            }
        }
        
        if(self.panAvatarLastTranslation.x > 0 && avatarBound["left"] > standardAvatarBound["left"]) {
            UIView.animateWithDuration(0.5, animations: {
                self.avatar.center.x -= (avatarBound["left"]! - standardAvatarBound["left"]!)
            })
            if (debugOutOfBounds) {
                print("Out of left bound")
            }
        }
        
        if(self.panAvatarLastTranslation.y > 0 && avatarBound["top"] > standardAvatarBound["top"]) {
            UIView.animateWithDuration(0.5, animations: {
                self.avatar.center.y -= (avatarBound["top"]! - standardAvatarBound["top"]!)
            })
            if (debugOutOfBounds) {
                print("Out of top bound")
            }
        }
        if(self.panAvatarLastTranslation.y < 0 && avatarBound["bottom"] < standardAvatarBound["bottom"]) {
            UIView.animateWithDuration(0.5, animations: {
                self.avatar.center.y += (standardAvatarBound["bottom"]! - avatarBound["bottom"]!)
            })
            if (debugOutOfBounds) {
                print("Out of bottom bound")
            }
        }
    }
    recognizer.setTranslation(CGPointZero, inView: view)
}

Okay, as you can see that I'm handling dragging inertia in the ".Ended" state of the pan gesture recognizer. It works well, but not smooth. What can I do to make a smooth panning inertia?

Here is the source code: https://github.com/AarioAi/NotesOpen/tree/master/Swift

like image 319
AarioAi Avatar asked Mar 12 '16 15:03

AarioAi


1 Answers

Try using different method for animating. Heck out this tutorial

like image 192
Sasha Kozachuk Avatar answered Oct 22 '22 01:10

Sasha Kozachuk