Codementor Events

Super Cool iOS Animation

Published Jan 30, 2019
Super Cool iOS Animation

Building custom animation in iOS can be quite easy in some cases but not every case. Some animations can be very complex.

This is the animation we will try to replicate. It looks easy doesn’t it? Because you can just animate of the position of the moving view, but sit back and think about how you are going to animate the view around in a circular form.

Two things to note before we get started:

  1. The four views in the center will be referred to as staticViews
  2. The view jumping up and down will be called movingView
    So to achieve the circular animation, my own approach will be to use UIBezierPath to draw the animation path (PS: this is my own approach. Doesn’t mean it’s the best). I drew the path out before hooking the bezier paths together

1_OivEwubPB78hw_tAfkgobw.png

I know, i can’t draw to save my own life 😂, but this is what my own bezier paths will look like and my staticViews will have the same frame withe same size but different origin because the x axis will be different Capeesh?

Now jumping into the Xcode, let’s code the bezier paths up

let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 25.5, y: 185.5))
bezierPath.addCurve(to: CGPoint(x: 25.5, y: 56.5), controlPoint1: CGPoint(x: 25.5, y: 185.5), controlPoint2: CGPoint(x: 25.5, y: 88.75))
bezierPath.addCurve(to: CGPoint(x: 72.5, y: 67.5), controlPoint1: CGPoint(x: 25.5, y: 24.25), controlPoint2: CGPoint(x: 60.75, y: 64.75))
bezierPath.addCurve(to: CGPoint(x: 108.5, y: 185.5), controlPoint1: CGPoint(x: 84.25, y: 70.25), controlPoint2: CGPoint(x: 108.5, y: 185.5))
bezierPath.addCurve(to: CGPoint(x: 136.5, y: 299.5), controlPoint1: CGPoint(x: 108.5, y: 185.5), controlPoint2: CGPoint(x: 129.5, y: 271))
bezierPath.addCurve(to: CGPoint(x: 186.5, y: 279.5), controlPoint1: CGPoint(x: 143.5, y: 328), controlPoint2: CGPoint(x: 174, y: 284.5))
bezierPath.addCurve(to: CGPoint(x: 202.5, y: 185.5), controlPoint1: CGPoint(x: 199, y: 274.5), controlPoint2: CGPoint(x: 202.5, y: 185.5))
bezierPath.addCurve(to: CGPoint(x: 217.5, y: 67.5), controlPoint1: CGPoint(x: 202.5, y: 185.5), controlPoint2: CGPoint(x: 213.75, y: 97))
bezierPath.addCurve(to: CGPoint(x: 261.5, y: 67.5), controlPoint1: CGPoint(x: 221.25, y: 38), controlPoint2: CGPoint(x: 261.5, y: 67.5))
bezierPath.addCurve(to: CGPoint(x: 291.5, y: 185.5), controlPoint1: CGPoint(x: 261.5, y: 67.5), controlPoint2: CGPoint(x: 284, y: 156))
bezierPath.addCurve(to: CGPoint(x: 335.5, y: 299.5), controlPoint1: CGPoint(x: 299, y: 215), controlPoint2: CGPoint(x: 335.5, y: 299.5))
UIColor.black.setStroke()
bezierPath.lineWidth = 0

Notice i made lineWidth 0 this is because we don’t want some black line showing up on the screen.

I also changed the position of the staticViews to the end point of each curve .

1_lq4hREVJONfU7wNYtCIvYw.png

Now the challenge is how to animate the movingView around the path. I thought I could just animate the movingView center or the position, but the result was not pleasant at all because it didn’t animate the curves and still had like a square edge, so i found a CAKeyframeAnimation key that let’s you animate along a path and it is called animate along path

let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = bezierPath.cgPath
animation.repeatCount = 0
animation.duration = 3.0
animation.delegate = self
movingView.layer.add(animation, forKey: "animate along path")
movingView.center = CGPoint(x: 0, y: frame.maxY)
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)

Running the app gave me this result:

1_HQsuMaAWUIe1_OT68BoR8g.gif

Boom! this is definitely the behavior i was expecting! ♨️

Now the next challenge is the water drop effect which is a bit tricky, but the easiest solution to this is change the scale of the movingView when it is transitioning from one staticView to another which means whenever it lands on a staticView it will change it’s scale which is pretty easy. All i had to do was to use the good old UIView animate

fileprivate func scaleMethod() {
    UIView.animate(withDuration: 0.75, animations: {
        self.movingView.transform = .identity
    }) { _ in
        self.movingView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        self.scaleMethod()
    }
}

Note that the original transform for movingView was CGAffineTransform(scaleX: 0.5, y: 0.5). Hopefully that makes sense.

Phew! so we have the scale animation and curvy animation done 🥳

Now if you noticed the movingView gradient changes when it get on a particular staticView. This is probably the easiest thing to do which is why i put it last. To achieve this i set a timer when the CAKeyframeAnimation above starts that run every 0.01 seconds which to check the current position of the movingView .

To get the the current frame of the movingView while animating, I had to get the presentationLayer

let frame = self.movingView.layer.presentationLayer()?.frame

if (f?.origin.x)! >= self.circleTwo.frame.origin.x{
rectV.applyGradient(colours: [UIColor(red:1.00, green:0.89, blue:0.35, alpha:1.0), UIColor(red:1.00, green:0.65, blue:0.32, alpha:1.0)])
}else if (f?.origin.x)! >= self.circleThree.frame.origin.x{
rectV.applyGradient(colours: [UIColor(red:0.47, green:0.62, blue:0.05, alpha:1.0), UIColor(red:0.67, green:0.73, blue:0.47, alpha:1.0)])
}else if (f?.origin.x)! >= self.circleFour.frame.origin.x{
rectV.applyGradient(colours: [UIColor(red:0.96, green:0.69,        blue:0.10, alpha:1.0), UIColor(red:0.95, green:0.15, blue:0.07, alpha:1.0)])
}

Finally the result

1_4orbT2VlpwWL17sPklrUYg.gif

Doesn’t look exactly like the original design. The water drop effect can be worked on! I will link the source code to the project below.

Discover and read more posts from Sagaya Abdulhafeez
get started
post commentsBe the first to share your opinion
Show more replies