核心动画简介:
核心动画直接作用于layer层;在后台线程执行,不阻塞主线程
一、CABasicAnimation (针对layer的属性进行动画效果)
1、位置动画:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let animation:CABasicAnimation = CABasicAnimation()
animation.keyPath = "position"
animation.duration = 2;
//下面代码表示动画结束后,保持结束时的状态
animation.fillMode = CAMediaTimingFillMode.forwards
animation.isRemovedOnCompletion = false
animation.toValue = NSValue(cgPoint: CGPoint(x: 300, y: 600))
layer?.add(animation, forKey: nil)
}
其他属性与上面一样,只要把要做的动画属性赋值给 animation.duration 即可
二、CAKeyframeAnimation 关键帧动画类
1、属性介绍:values : 一系列位置信息; keyTimes : 默认是均匀播放的,取值范围:0-1;
path : 绘制动画路径
2、介绍下 淡入淡出动画
class ViewController: UIViewController {
var myView:UIView?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
let myV = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
myV.center = view.center
myV.backgroundColor = UIColor.blue
view.addSubview(myV)
myView = myV;
let animation:CAKeyframeAnimation = CAKeyframeAnimation()
animation.duration = 10
animation.keyPath = "opacity"
let values:[NSNumber] = [NSNumber(value: 0.95),NSNumber(value: 0.90),NSNumber(value: 0.85),NSNumber(value: 0.80),
NSNumber(value: 0.75),NSNumber(value: 0.70),NSNumber(value: 0.65),NSNumber(value: 0.60),NSNumber(value: 0.50),NSNumber(value: 0.40),NSNumber(value: 0.30),NSNumber(value: 0.20)]
animation.values = values
animation.fillMode = CAMediaTimingFillMode.forwards
animation.isRemovedOnCompletion = false
myV.layer.add(animation, forKey: nil)
}
}
其他属性动画类似,修改values和keypath 即可
3、任意路径动画
let animation:CAKeyframeAnimation = CAKeyframeAnimation()
animation.duration = 10
let path = CGMutablePath()
path.move(to: CGPoint(x: 40, y: 40))
path.addLine(to: CGPoint(x: 300, y: 300))
path.addLine(to: CGPoint(x: 40, y: 600))
animation.path = path
animation.keyPath = "position";
animation.fillMode = CAMediaTimingFillMode.forwards
animation.isRemovedOnCompletion = false
myV.layer.add(animation, forKey: nil)
4、 CAAnimationGroup 组合动画介绍
let ca1 = CABasicAnimation(keyPath:"transform.rotation")
ca1.toValue = Double.pi
let scale = CABasicAnimation(keyPath: "transform.scale")
scale.toValue = 0.1
let move = CABasicAnimation(keyPath: "transform.translation")
move.toValue = NSValue(cgPoint: view.center)
let animaGroup = CAAnimationGroup()
animaGroup.animations = [ca1,scale,move]
animaGroup.duration = 6
animaGroup.fillMode = CAMediaTimingFillMode.forwards
animaGroup.isRemovedOnCompletion = false
myV.layer.add(animaGroup, forKey: nil)
三、综合案例的实现
1、水纹按钮动画效果的实现
主要分成胰腺癌5个主要模块
1)UIBUtton的坐标获取
2)Draw 圆形绘制模块
3)定时器刷新
4)其他模块
5)调用模块
代码如下:代码主要在自定义button中实现
import UIKit
class MyButton: UIButton {
var timer:Timer?
var touchPoint:CGPoint?
var viewRadius:CGFloat = 0
var targetAnimationColor:UIColor = UIColor(red: 216.0/255.0, green: 114.0/255.0, blue: 213.0/255.0, alpha: 0.8)
var countNum:Int = 0
override init(frame: CGRect) {
super.init(frame:frame)
backgroundColor = UIColor(red: 50.0/255.0, green: 185.0/255.0, blue: 170.0/255.0, alpha: 1.0)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//此处禁用按钮点击事件,获取点击位置,
func starButtonAnimation(send:UIButton,event:UIEvent) {
isUserInteractionEnabled = false;
let button:UIView = send as UIView
let touchSet:NSSet = event.touches(for: button)! as NSSet
var touchArray:[AnyObject] = touchSet.allObjects as [AnyObject]
let touch1:UITouch = touchArray[0] as! UITouch
let point:CGPoint = touch1.location(in: button)
touchPoint = point
timer = Timer.scheduledTimer(timeInterval: 0.2, target: send, selector: #selector(timeaction), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: RunLoop.Mode.common)
}
//定时器方法,定时刷新上下重新绘制
@objc func timeaction() {
countNum += 1
let dismissTime:DispatchTime = DispatchTime.now() + Double(bitPattern: 0*NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: dismissTime) {
self.viewRadius += 4
self.setNeedsDisplay()
}
if countNum>70 {
countNum = 0
viewRadius = 0
timer?.invalidate()
DispatchQueue.main.asyncAfter(deadline: dismissTime) {
self.viewRadius = 0
self.setNeedsDisplay()
}
self.isUserInteractionEnabled = true
}
}
//绘制圆
override func draw(_ rect: CGRect) {
let ctx:CGContext = UIGraphicsGetCurrentContext()!
let endAngle:CGFloat = CGFloat(Double.pi*2)
ctx.addArc(center: touchPoint ?? center, radius:viewRadius , startAngle: 0, endAngle: endAngle, clockwise: false)
let stockColor:UIColor = targetAnimationColor
stockColor.setStroke()
stockColor.setFill()
ctx.fillPath()
}
}
//以上为自定义按钮的实现内用,下面我控制器内使用的代码
class ViewController: UIViewController {
var myView:UIView?
var image:UIImageView?
var myBtn:MyButton?
override func viewDidLoad() {
super.viewDidLoad()
let mBtn:MyButton = MyButton(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
view.addSubview(mBtn)
mBtn.center = view.center
myBtn = mBtn
mBtn.backgroundColor = UIColor.yellow
mBtn.setTitle("登录", for: UIControl.State.normal)
mBtn.addTarget(self, action: #selector(buttonClick(_:event:)), for: UIControl.Event.touchUpInside)
}
@objc func buttonClick(_ sender:UIButton,event:UIEvent) {
let bt:MyButton = sender as! MyButton
bt.starButtonAnimation(send: sender, event: event)
}
}
四、CAShapeLayer 主要用于各种图形绘制(如柱状图、饼状图、股票的K线图等等)
都是基于贝济埃曲线绘制,首先来了解什么是贝济埃曲线
1、贝济埃曲线,它是依据四个位置任意点的坐标绘制出一条光滑的曲线(如四个顶点A、B、C、D,其中A为绘制起点,D为终点,在A处设置一条曲线,保证曲线的切刚好经过B,同时在D出设置一条曲线,刚好保证曲线经过C点)
2、贝济埃曲线的使用步骤:
1)创建一个贝济埃曲线对象(UIBezierPath)
2) 设置曲线的各种属性:颜色、线条粗细等
3)设置曲线起始点
4)开始绘制
3、UIBezierPath 的属性介绍
1)线条宽度 lineWidth
2) 线条拐角 lineCapStyle (设置线条首位形状的)
lineJoinStyle 用于设置两条线段衔接处的
func drawLine() {
//设置路径
let path = UIBezierPath()
path.move(to: CGPoint(x: 20, y: 40))
path.addLine(to: CGPoint(x: 200, y: 200))
path.addLine(to: CGPoint(x: 40, y: 400))
path.lineCapStyle = CGLineCap.round
path.close()
//开始绘画
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = 10
shapeLayer.lineCap = .round
shapeLayer.lineJoin = .round
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.yellow.cgColor
shapeLayer.path = path.cgPath
view.layer.addSublayer(shapeLayer)
}
4、绘制动态图表
1)折线图
import UIKit
class MyChartLine: UIView {
var charLine:CAShapeLayer = CAShapeLayer()
var pathAnimation:CABasicAnimation = CABasicAnimation()
init(frame: CGRect,path:UIBezierPath) {
super.init(frame: frame)
backgroundColor = UIColor.white
clipsToBounds = true
charLine.lineCap = .round
charLine.lineJoin = .round
charLine.fillColor = UIColor.white.cgColor
charLine.strokeColor = UIColor.green.cgColor
charLine.lineWidth = 3
//这个属性设置是为了,让线不是一次性绘画完成
charLine.strokeEnd = 0.0
charLine.path = path.cgPath
layer.addSublayer(charLine)
pathAnimation.keyPath = "strokeEnd"
pathAnimation.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.linear)
pathAnimation.fromValue = 0.0
pathAnimation.toValue = 1.0
pathAnimation.autoreverses = false
pathAnimation.duration = 5
pathAnimation.fillMode = CAMediaTimingFillMode.forwards
pathAnimation.isRemovedOnCompletion = false
charLine.add(pathAnimation, forKey: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
以上代码,是用于折线的view的内部代码,实现了line和动画,外界只要传个路径就可以
import UIKit
class MyController1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
let path:UIBezierPath = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 500))
path.addLine(to: CGPoint(x: 40, y: 60))
path.addLine(to: CGPoint(x: 140, y: 300))
path.addLine(to: CGPoint(x: 190, y: 290))
path.addLine(to: CGPoint(x: 240, y: 360))
path.addLine(to: CGPoint(x: 290, y: 180))
path.addLine(to: CGPoint(x: 360, y: 60))
let myLine:MyChartLine = MyChartLine(frame: CGRect(x:0, y: 500, width: UIScreen.main.bounds.size.width, height: 400), path: path)
view.addSubview(myLine)
}
}