Swift动画 —— 登陆页面动画(二)

207 阅读3分钟

下面是想要实现的动画效果:

在这里插入图片描述

Logo的动画做完了,接下来要做中间画线的动画以及中间Button的浮现动画 同样的,在这里声明loginButton属性和所需要的button大小属性。

    private let loginButtonSize = CGSize(width: 60, height: 60)
    var loginButton: UIButton!

在setupUI里面设置好loginButton的大小、位置、border、cornerRadius、title等属性并添加到为view的subview。

  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)

接下来就需要先画线,用UIBezierPath来画出所期望的线。

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

接下来根据画的线来创建shapeLayer并设置好各种属性。

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 = 1
view.layer.addSublayer(shapeLayer)

现在的效果:

在这里插入图片描述

接下来只需要简单的为其添加一个动画就可以了,这里需要把前面的shapeLayer.strokeEnd 改为0。

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
shapeLayer.add(animation, forKey: animation.keyPath)

到这里,画线的动画就完成啦。就剩下button的浮现动画啦。那么这里有个问题,button的浮现动画需要在画线的动画之后进行,那么如何确保button的浮现动画在画线的动画之后进行呢?这里就需要用到CATransaction。代码如下:

   CATransaction.begin()
        CATransaction.setCompletionBlock({
            UIView.animate(withDuration: 0.5, animations: {
                self.loginButton.alpha = 1.0
            })
        })
        shapeLayer.add(animation, forKey: animation.keyPath)
        CATransaction.commit()

到这里, 登陆页面动画的logo部分以及中间部分的动画都已经完成了,也就是2/3的工作已经完成了,接下来就只剩下🚗 的动画啦。 现在的效果:

请添加图片描述

完整代码:

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!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setupUI()
        animateLogo {
            self.animateLoginButton()
        }
    }
    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)


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