最近正在研究iOS动画效果的实现,目的也是为了自己能够写出比较炫酷的动画效果。趁着项目不怎么忙,抽出时间写篇文章来记录一下自己的学习成果及实战效果。由于本人是最近才开始写博客,不善言辞,不喜勿喷,如果错误,还请指正。
本篇文章的动画效果也是我在学习动画效果的过程中其中一个页面的动画效果。
效果展示
非常简单的一个按钮动画效果,有兴趣的同学可以自己动手实现一下。我再这里界面布局是用的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
}
}