• Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Here are the animations you want to achieve:Which leaves 🚗. The animation of. First you need to put 🚗. Put the image in Asset. Also declare a carImageView property and the desired image size property. Here we need to create an additional carAnimator for the animation.

private let carSize = CGSize(width: 50, height: 45) private var carImageView: UIImageView! Private var carAnimator: UIViewPropertyAnimator = UIViewPropertyAnimator(Duration: 3.0, Curve:.easeinout, animations: nil)Copy the code

Again, you need to set the size of the carImageView and other properties in setupUI, then make it interactive, add click gestures and response methods to animate the click later, and finally add a child view of the view.

  carImageView = UIImageView(frame: CGRect(x: 0, y: view.frame.height - 100, width: carSize.width, height: carSize.height))
        carImageView.contentMode = .scaleToFill
        carImageView.image = UIImage(named: "car_icon")
        carImageView.isUserInteractionEnabled = true
        carImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(userTapOnCar)))
        view.addSubview(carImageView)
Copy the code

Declare an animateCarToRight method by adding 🚗. AnimateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo: animateLogo

private func animateCarToRight() { carAnimator.stopAnimation(true) carAnimator.addAnimations { // Move car to the bottom  right of the screen self.carImageView.frame = CGRect(origin: CGPoint(x: self.view.frame.width - self.carSize.width, y: self.view.frame.height - 100), size: self.carSize) } carAnimator.addCompletion { _ in UIView.animate(withDuration: 0.5, animations: {self. CarImageView. Transform = CGAffineTransform (scaleX: 1.0, y: 1.0)}, completion: { _ in }) } carAnimator.startAnimation() }Copy the code

Next in carImageView judgment carImageView. Click on the response of the method to add frame. The origin. The x and the frame. The width / 2 code, if is greater than the need to move from right to left, less than moving from left to right.

    @objc private func userTapOnCar() {
        if carImageView.frame.origin.x > view.frame.width/2 {
            // Destination is on the right, go back to the left
            self.animateCarToLeft()
        } else {
            // Destination is on the left, go back to the right
            self.animateCarToRight()
        }
    }
Copy the code

We also need to add the animateCarToLeft method, which is similar to the animateCarToRight method, except that we need to change the x value to be moved and the transform value to be flipped.

private func animateCarToLeft() { carAnimator.stopAnimation(true) carAnimator.addAnimations { // Move car to the bottom left of the screen self.carImageView.frame = CGRect(origin: CGPoint(x: 0, y: self.view.frame.height - 100), size: Self. CarSize)} carAnimator. AddCompletion {_ in UIView. The animate (withDuration: 0.5, animations: {self. CarImageView. Transform = CGAffineTransform (scaleX: 1.0, y: 1.0)}, completion: { _ in self.animateCarToRight() }) } carAnimator.startAnimation() }Copy the code

This is where the landing page animation is done. Complete code:

import UIKit

class ViewController: UIViewController {

    private let logoSize = CGSize(width: 250, height: 40)
    private var logoImageView: UIImageView!
    
    private let loginButtonSize = CGSize(width: 60, height: 60)
    var loginButton: UIButton!
    
    private let carSize = CGSize(width: 50, height: 45)
    private var carImageView: UIImageView!
    private var carAnimator: UIViewPropertyAnimator = UIViewPropertyAnimator(duration: 3.0, curve: .easeInOut, animations: nil)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setupUI()
        animateLogo {
            self.animateLoginButton()
            self.animateCarToRight()
        }
    }
    private func setupUI() {

        // Initialize the logo at the center of screen
        logoImageView = UIImageView(frame: CGRect(x: view.frame.width/2 - logoSize.width/2,
                                                  y: view.frame.height/2 - logoSize.height/2,
                                                  width: logoSize.width,
                                                  height: logoSize.height))
        logoImageView.contentMode = .scaleToFill
        logoImageView.image = UIImage(named: "logo_icon")
        logoImageView.isUserInteractionEnabled = true
        logoImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(shakeLogo)))
        view.addSubview(logoImageView)
        
        // Initialize the logo at the center of screen
        loginButton = UIButton(type: .custom)
        loginButton.frame = CGRect(x: view.frame.width/2 - loginButtonSize.width/2,
                                   y: view.frame.height/2 - loginButtonSize.height/2,
                                   width: loginButtonSize.width,
                                   height: loginButtonSize.height)
        loginButton.center = view.center
        loginButton.layer.borderColor = UIColor.brown.cgColor
        loginButton.layer.borderWidth = 1
        loginButton.layer.cornerRadius = loginButtonSize.width / 2
        loginButton.setTitle("Open", for: .normal)
        loginButton.setTitleColor(.brown, for: .normal)
        loginButton.setTitleColor(UIColor.brown.withAlphaComponent(0.5), for: .highlighted)
        loginButton.alpha = 0
        view.addSubview(loginButton)
        
        carImageView = UIImageView(frame: CGRect(x: 0, y: view.frame.height - 100, width: carSize.width, height: carSize.height))
        carImageView.contentMode = .scaleToFill
        carImageView.image = UIImage(named: "car_icon")
        carImageView.isUserInteractionEnabled = true
        carImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(userTapOnCar)))
        view.addSubview(carImageView)


    }
    
    
    
    @objc private func shakeLogo() {
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.07
        animation.repeatCount = 4
        animation.autoreverses = true
        animation.fromValue = NSValue(cgPoint: CGPoint(x: logoImageView.center.x - 10, y: logoImageView.center.y))
        animation.toValue = NSValue(cgPoint: CGPoint(x: logoImageView.center.x + 10, y: logoImageView.center.y))
        logoImageView.layer.add(animation, forKey: "position")
    }
    private func animateLoginButton() {
        // Step 1: Draw the Bezier path
        let path = UIBezierPath()

        // Start at center left of screen
        path.move(to: CGPoint(x: 0, y: view.center.y))

        // Add line to the left side -10 px of login button
        path.addLine(to: CGPoint(x: loginButton.center.x - loginButtonSize.width - 10,
                                 y: view.center.y))

        // Add arc that go to -10 px bottom of login button
        path.addArc(withCenter: loginButton.center,
                    radius: loginButtonSize.height/2 + 10,
                    startAngle: CGFloat(Double.pi),
                    endAngle: CGFloat(Double.pi/2),
                    clockwise: false)

        // Add arc that go to 10 px right of login button
        path.addArc(withCenter: loginButton.center,
                    radius: loginButtonSize.height/2 + 10,
                    startAngle: CGFloat(Double.pi/2),
                    endAngle: CGFloat(0),
                    clockwise: false)

        // Add line to the center right of screen
        path.addLine(to: CGPoint(x: view.frame.width,
                                 y: view.center.y))

        // Step 2: Create the shape layer from Bezier path
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.brown.cgColor
        shapeLayer.lineWidth = 1
        shapeLayer.lineCap = CAShapeLayerLineCap.round
        shapeLayer.lineJoin = CAShapeLayerLineJoin.round
        shapeLayer.strokeEnd = 0
        view.layer.addSublayer(shapeLayer)
        
        
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.toValue = 1
        animation.duration = 2.0
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        animation.fillMode = CAMediaTimingFillMode.both // keep to value after finishing
        animation.isRemovedOnCompletion = false // don't remove after finishing
        CATransaction.begin()
        CATransaction.setCompletionBlock({
            UIView.animate(withDuration: 0.5, animations: {
                self.loginButton.alpha = 1.0
            })
        })
        shapeLayer.add(animation, forKey: animation.keyPath)
        CATransaction.commit()

    }
    
    private func animateLogo(completion: @escaping ()->()) {
        UIView.animate(withDuration: 1.0, animations: {
            self.logoImageView.frame = self.logoImageView.frame.offsetBy(dx: 0, dy: -250)
        }, completion: { _ in
            completion()
        })
    }
    
    @objc private func userTapOnCar() {
        if carImageView.frame.origin.x > view.frame.width/2 {
            // Destination is on the right, go back to the left
            self.animateCarToLeft()
        } else {
            // Destination is on the left, go back to the right
            self.animateCarToRight()
        }
    }
    
    
    private func animateCarToRight() {
        carAnimator.stopAnimation(true)
        carAnimator.addAnimations {
            // Move car to the bottom right of the screen
            self.carImageView.frame = CGRect(origin: CGPoint(x: self.view.frame.width - self.carSize.width,
                                                             y: self.view.frame.height - 100),
                                             size: self.carSize)
        }
        carAnimator.addCompletion { _ in
            UIView.animate(withDuration: 0.5, animations: {
                self.carImageView.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
            }, completion: { _ in
            })
        }
        carAnimator.startAnimation()
    }
    
    private func animateCarToLeft() {
        carAnimator.stopAnimation(true)
        carAnimator.addAnimations {
            // Move car to the bottom left of the screen
            self.carImageView.frame = CGRect(origin: CGPoint(x: 0,
                                                             y: self.view.frame.height - 100),
                                             size: self.carSize)
        }
        carAnimator.addCompletion { _ in
            UIView.animate(withDuration: 0.5, animations: {
                self.carImageView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
            }, completion: { _ in
                self.animateCarToRight()
            })
        }
        carAnimator.startAnimation()
    }
}
Copy the code