iOS动画效果实战篇之CABasicAnimation的使用

最近正在研究iOS动画效果的实现,目的也是为了自己能够写出比较炫酷的动画效果。趁着项目不怎么忙,抽出时间写篇文章来记录一下自己的学习成果及实战效果。由于本人是最近才开始写博客,不善言辞,不喜勿喷,如果错误,还请指正。
本篇文章的动画效果也是我在学习动画效果的过程中其中一个页面的动画效果。

##效果展示
CABaseAnimation.gif

非常简单的一个按钮动画效果,有兴趣的同学可以自己动手实现一下。我再这里界面布局是用的Stroyboard,手写代码的话也非常简单。

##布局

布局我就不多说了,拖几个控件再把约束加上就行了,这里主要说一下登录按钮的约束。
1.左右两边约束。2.居中约束。3.高度约束。4.宽高比约束。
这里注意一点,在Storyboard中将登录按钮右侧的约束的Installed属性对勾去掉,否则会有警告信息,不过并不影响效果

下面我就直接上代码了。我的编译环境是Xcode10 + Swift4.2,OC版本自己转化一下就可以了

下面是ViewController中代码

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var loginButton: UIButton!
    @IBOutlet weak var ratioConstraint: NSLayoutConstraint!//宽高比约束
    @IBOutlet weak var rightConstraint: NSLayoutConstraint!//右侧约束
    @IBOutlet weak var leftConstraint: NSLayoutConstraint!//左侧约束

    //旋转动画效果的图层,动画的旋转效果都在这个图层中实现
    private var testLayer: ActivityLayer!
    
    private var isAnimation: Bool = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.loginButton.layer.cornerRadius = 22.5
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }

    @IBAction func clickLogin(_ sender: UIButton) {
        
        //模拟网络请求
        self.shrinkLoginButton()
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.spreadLoginButton()
        }
    }
    //按钮缩小动画
    private func shrinkLoginButton() {
        
        self.leftConstraint.isActive = false
        self.rightConstraint.isActive = false
        
        let ratioC = NSLayoutConstraint(item: self.loginButton, attribute:NSLayoutConstraint.Attribute.width , relatedBy:.equal , toItem: self.loginButton, attribute: NSLayoutConstraint.Attribute.height, multiplier: 1.0, constant: 0)
        ratioC.isActive = true
        self.ratioConstraint = ratioC
        
        UIView.animate(withDuration: 0.25, animations: {
            self.view.layoutIfNeeded()
            self.loginButton.setTitleColor(UIColor(white: 1, alpha: 0), for: .normal)
        }) { (finished) in
            if finished {
                self.testLayer = ActivityLayer.init(self.loginButton.bounds)
                self.loginButton.layer.addSublayer(self.testLayer)
                self.testLayer.startAnimation()
            }
        }
    }
    //按钮拉长动画
    private func spreadLoginButton() {
        
        self.ratioConstraint.isActive = false
        
        let leftC = NSLayoutConstraint(item: self.loginButton, attribute: NSLayoutConstraint.Attribute.left, relatedBy: .equal, toItem: self.loginButton.superview, attribute: NSLayoutConstraint.Attribute.left, multiplier: 1.0, constant: 25.0)
        let rightC = NSLayoutConstraint(item: self.loginButton, attribute: NSLayoutConstraint.Attribute.right, relatedBy: .equal, toItem: self.loginButton.superview, attribute: NSLayoutConstraint.Attribute.right, multiplier: 1.0, constant: -25.0)
        
        leftC.isActive = true
        rightC.isActive = true
        self.leftConstraint = leftC
        self.rightConstraint = rightC
        
        self.testLayer.removeFromSuperlayer()
        UIView.animate(withDuration: 0.25) {
            self.view.layoutIfNeeded()
            self.loginButton.setTitleColor(UIColor(white: 1, alpha: 1), for: .normal)
        }
    }
    
}

以下是ActivityLayer的实现

import UIKit
class ActivityLayer: CALayer {
    
    let spaceWidth: CGFloat = 3.0
    let lineWidth: CGFloat = 3.0

    private lazy var shaperLayer: CAShapeLayer = {
        
        let path = UIBezierPath(arcCenter: CGPoint.zero, radius: self.frame.height / 2.0 - spaceWidth - lineWidth, startAngle: CGFloat(-Double.pi / 2), endAngle: 0, clockwise: true)
        let shaperLayer = CAShapeLayer()
        shaperLayer.position = self.position//如果不加这句那shaperLayer会绕着(0,0)点旋转。
        shaperLayer.fillColor = UIColor.clear.cgColor
        shaperLayer.strokeColor = UIColor.white.cgColor
        shaperLayer.lineWidth = lineWidth
        shaperLayer.path = path.cgPath
        return shaperLayer
    }()
    
    override init() {
        super.init()
    }
    
    convenience init(_ frame: CGRect) {
        self.init()
        self.frame = frame
        self.addSublayer(self.shaperLayer)
        
        let baseAnimation = CABasicAnimation()
        baseAnimation.duration = 0.25
        baseAnimation.keyPath = "transform"
        baseAnimation.isRemovedOnCompletion = false
        baseAnimation.fillMode = .forwards
        baseAnimation.isCumulative = true
        baseAnimation.repeatCount = MAXFLOAT
        //CATransform3DMakeRotation 当顺时针和逆时针路径相同时,总是使用逆时针,此时设置z轴的正负没有效果
        //CATransform3DMakeRotation(CGFloat(Double.pi), 0, 0, 1)以及CATransform3DMakeRotation(CGFloat(Double.pi), 0, 0, -1)都是逆时针
        let transform = CATransform3DMakeRotation(CGFloat(Double.pi) / 2, 0, 0, 1)
        baseAnimation.toValue = NSValue(caTransform3D: transform)
        self.shaperLayer.add(baseAnimation, forKey: nil)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func startAnimation() {
        
        let pauseTime = self.shaperLayer.timeOffset
        self.shaperLayer.speed = 1
        self.shaperLayer.timeOffset = 0
        self.shaperLayer.beginTime = 0
        
        let timeSincePause = self.shaperLayer.convertTime(CACurrentMediaTime(), from: nil) - pauseTime
        self.shaperLayer.beginTime = timeSincePause
    }
    
    func stopAnimation() {
        
        let pausedTime = self.shaperLayer.convertTime(CACurrentMediaTime(), from: nil)
        self.shaperLayer.speed = 0.0
        self.shaperLayer.timeOffset = pausedTime
    }
}

猜你喜欢

转载自blog.csdn.net/chaoaifan/article/details/82907855