UIView本身对于基本动画和关键帧动画、转场动画都有相应的封装,在对动画细节没有特殊要求的情况下使用起来也要简单的多。可以说在日常开发中90%以上的情况使用UIView的动画封装方法都可以搞定.
常见动画设置方法
//标记动画的开始
open class func beginAnimations(_ animationID: String?, context: UnsafeMutableRawPointer?)
//提交动画
open class func commitAnimations()
//设置动画代理对象,当动画开始或者结束时会发消息给代理对象
open class func setAnimationDelegate(_ delegate: Any?)
//当动画即将开始时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
open class func setAnimationWillStart(_ selector: Selector?)
//当动画结束时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
open class func setAnimationDidStop(_ selector: Selector?)
//动画的持续时间,秒为单位
open class func setAnimationDuration(_ duration: TimeInterval)
//动画延迟delay秒后再开始
open class func setAnimationDelay(_ delay: TimeInterval)
//动画的开始时间,默认为now
open class func setAnimationStart(_ startDate: Date)
//动画的时间变化类型
open class func setAnimationCurve(_ curve: UIView.AnimationCurve)
//动画的重复次数
open class func setAnimationRepeatCount(_ repeatCount: Float)
//如果设置为YES,代表动画每次重复执行的效果会跟上一次相反
open class func setAnimationRepeatAutoreverses(_ repeatAutoreverses: Bool)
//设置视图view的过渡效果, transition指定过渡类型, cache设置YES代表使用视图缓存,性能较好.
open class func setAnimationTransition(_ transition: UIView.AnimationTransition, for view: UIView, cache: Bool)
//是否允许动画
open class func setAnimationsEnabled(_ enabled: Bool)
可动画属性
-
bounds
: Animate this property to reposition the view’s content within the view’s frame. -
frame
: Animate this property to move and/or scale the view. -
center
: Animate this property when you want to move the view to a new location on screen. -
backgroundColor
: Change this property of a view to have UIKit gradually change the tint of the background color over time. -
alpha
: Change this property to create fade-in and fade-out effects. -
transform
: Modify this property within an animation block to animate the rotation, scale, and/or position of a view.
动画的方式
1、使用必包方式
UIView.animate(withDuration: 1, animations: {
// animate code
}, completion: { finished in
// animation finished
})
2、使用begin和commit方式
UIView.beginAnimations("animate", context: nil)
UIView.setAnimationDuration(0.5)
animationView.backgroundColor = UIColor.cyan
UIView.commitAnimations()
动画的类型
基本动画
使用UIView进行动画非常简单,iOS 提供了很方便的函数
extension UIView {
@available(iOS 4.0, *)
open class func animate(withDuration duration: TimeInterval,
delay: TimeInterval,
options: UIViewAnimationOptions = [],
animations: @escaping () -> Swift.Void,
completion: ((Bool) -> Swift.Void)? = nil)
@available(iOS 4.0, *)
open class func animate(withDuration duration: TimeInterval,
animations: @escaping () -> Swift.Void,
completion: ((Bool) -> Swift.Void)? = nil) // delay = 0.0, options = 0
@available(iOS 4.0, *)
open class func animate(withDuration duration: TimeInterval,
animations: @escaping () -> Swift.Void) // delay = 0.0, options = 0, completion = NULL
}
duration:动画的持续时间
delay:延迟动画时间
options:动画进行的方式,不同的选项直接可以通过“与”操作进行合并,同时使用,可以分为两类:
控制速度:
//动画先缓慢,然后逐渐加速,再变慢
public static var curveEaseInOut: UIViewAnimationOptions { get } //default
//
public static var curveEaseIn: UIViewAnimationOptions { get }
//动画开始快,后面慢
public static var curveEaseOut: UIViewAnimationOptions { get }
//动画匀速执行
public static var curveLinear: UIViewAnimationOptions { get }
如图所示
控制过程:
public static var allowUserInteraction: UIViewAnimationOptions { get } // turn on user interaction while animating
public static var beginFromCurrentState: UIViewAnimationOptions { get } // start all views from current value, not initial value
public static var `repeat`: UIViewAnimationOptions { get } // repeat animation indefinitely
public static var autoreverse: UIViewAnimationOptions { get } // if repeat, run animation back and forth
animations:动画闭包,在 animations中对 UIView 的属性进行动画
completion:动画完成闭包
移动图片
实现一个简单的需求,点击屏幕移动图片
闭包方式
class UIViewAnimationController: UIViewController {
lazy var imageView: UIImageView = {
let imageV = UIImageView(image: UIImage(named: "man"))
return imageV
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
view.addSubview(imageView)
imageView.center = view.center
}
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: view)
// 根据需求选择对应的方法设置对应的属性
UIView.animate(withDuration: 0.5) {
self.imageView.center = location
}
}
}
提交动画方式
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: view)
// 根据需求设置相应属性
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(0.5)
self.imageView.center = location
UIView.commitAnimations()
}
效果如下
关键帧动画
有些时候我们需要自己设定若干关键帧,实现更复杂的动画效果,这时候就需要关键帧动画的支持了
extension UIView {
@available(iOS 7.0, *)
open class func animateKeyframes(withDuration duration: TimeInterval,
delay: TimeInterval,
options: UIViewKeyframeAnimationOptions = [],
animations: @escaping () -> Swift.Void,
completion: ((Bool) -> Swift.Void)? = nil)
// start time and duration are values between 0.0 and 1.0 specifying time and duration
// relative to the overall time of the keyframe animation
@available(iOS 7.0, *)
open class func addKeyframe(withRelativeStartTime frameStartTime: Double,
relativeDuration frameDuration: Double,
animations: @escaping () -> Swift.Void)
}
同样是一张图片显示在界面上,点击界面触发关键帧动画。实现如下,会按照设置的时间进行动画
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
UIView.animateKeyframes(withDuration: 5.0, delay: 0.0, options: .calculationModeLinear, animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, //相对于5秒所开始的时间(第0秒开始动画)
relativeDuration: 0.5, //相对于5秒所持续时间(也就是5.0*0.5=2.5秒)
animations: {
self.imageView.center = CGPoint(x: 100, y: 220)
})
UIView.addKeyframe(withRelativeStartTime: 0.5, //相对于5秒所持续时间(也就是5.0*0.5=2.5秒)
relativeDuration: 0.25, //相对于5秒所持续时间(持续5.0*0.25=1.25秒)
animations: {
self.imageView.center = CGPoint(x: 60, y: 300)
})
UIView.addKeyframe(withRelativeStartTime: 0.75, //相对于5秒所开始的时间(从0.75*5.0秒开始)
relativeDuration: 0.25, //相对于5秒所持续时间(持续5.0*0.25=1.25秒)
animations: {
self.imageView.center = CGPoint(x: 120, y: 440)
})
}, completion: { finished in
print("animation finished")
})
}
过渡动画
过渡动画API
@available(iOS 4.0, *)
open class func transition(with view: UIView,
duration: TimeInterval,
options: UIViewAnimationOptions = [],
animations: (() -> Swift.Void)?,
completion: ((Bool) -> Swift.Void)? = nil)
// toView added to fromView.superview, fromView removed from its superview
@available(iOS 4.0, *)
open class func transition(from fromView: UIView,
to toView: UIView,
duration: TimeInterval,
options: UIViewAnimationOptions = [],
completion: ((Bool) -> Swift.Void)? = nil)
转场类型(仅适用于转场动画设置,可以从中选择一个进行设置,基本动画、关键帧动画不需要设置)
UIViewAnimationOptionTransitionNone:没有转场动画效果。
UIViewAnimationOptionTransitionFlipFromLeft :从左侧翻转效果。
UIViewAnimationOptionTransitionFlipFromRight:从右侧翻转效果。
UIViewAnimationOptionTransitionCurlUp:向后翻页的动画过渡效果。
UIViewAnimationOptionTransitionCurlDown :向前翻页的动画过渡效果。
UIViewAnimationOptionTransitionCrossDissolve:旧视图溶解消失显示下一个新视图的效果。
UIViewAnimationOptionTransitionFlipFromTop :从上方翻转效果。
UIViewAnimationOptionTransitionFlipFromBottom:从底部翻转效果。
单视图的转场动画
点击屏幕切换头像
lazy var imageView: UIImageView = {
let imageV = UIImageView(image: UIImage(named: "man"))
return imageV
}()
var images = ["1","2","3","4","man"]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
view.addSubview(imageView)
imageView.center = view.center
}
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
UIView.transition(with: imageView, duration: 1.0, options: .transitionCrossDissolve, animations: {
let random = arc4random_uniform(UInt32(self.images.count))
self.imageView.image = UIImage(named: self.images[Int(random)])
}, completion: nil)
}
效果如下
双视图转场动画
简单实现视图之间的切换
class ViewTransitionController: UIViewController {
lazy var subView1: UIView = {
let view = UIView(frame: self.view.bounds)
view.backgroundColor = UIColor.red
return view
}()
lazy var subView2: UIView = {
let view = UIView(frame: self.view.bounds)
view.backgroundColor = UIColor.blue
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(subView1)
}
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
var toView: UIView?
var fromView: UIView?
if subView1.superview == nil {
toView = subView1
fromView = subView2
} else {
toView = subView2
fromView = subView1
}
UIView.transition(from: fromView!, to: toView!, duration: 1.0, options: .transitionFlipFromLeft) { finished in
print("subView1:\(self.subView1.superview) subView2: \(self.subView2.superview)")
}
}
}
效果如下
弹性动画
@available(iOS 7.0, *)
open class func animate(withDuration duration: TimeInterval,
delay: TimeInterval,
usingSpringWithDamping dampingRatio: CGFloat,
initialSpringVelocity velocity: CGFloat,
options: UIView.AnimationOptions = [],
animations: @escaping () -> Void,
completion: ((Bool) -> Void)? = nil)
springDamping
弹性阻尼,取值范围时 0 到 1,越接近 0 ,动画的弹性效果就越明显;如果设置为 1,则动画不会有弹性效果
initialSpringVelocity
数值越小,动力越小,弹簧的拉伸幅度就越小。反之相反。比如:总共的动画运行距离是200 pt,你希望每秒 100pt 时,值为 0.5;
在 initialSpringVelocity 为 0 ,damping 分别为 0.4,0.6,0.8 的情况下效果如图,可见阻尼越小,弹簧效果越明显