UIView动画也称为隐式动画,动画过程即frame等属性发生改变的过程,主要包括首尾式动画(UIViewAnimation)、block动画(UIViewAnimationWithBlocks)、帧动画(UIViewKeyframeAnimations)
一、首尾式动画(UIViewAnimation)
通常情况,如果只是修改控件的属性,使用首尾式动画还是比较方便的,但是如果需要在动画完成后做后续处理,就不是那么方便了,这时可以使用block动画进行操作。
// 开始动画,animationID是给该动画一标识相当于起了一个名字,context是传递给 启动/停止选择器 的信息
+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;
// 启动动画
+ (void)commitAnimations;
//设置动画代理,默认为nil
+ (void)setAnimationDelegate:(nullable id)delegate;
//设置动画将要开始时的选择器方法。推荐方法: -animationWillStart:(NSString *)animationID context:(void *)context
+ (void)setAnimationWillStartSelector:(nullable SEL)selector;
// 设置动画已经结束时的选择器方法。推荐方法: -animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
+ (void)setAnimationDidStopSelector:(nullable SEL)selector;
// 设置动画持续时间,默认为0.2秒
+ (void)setAnimationDuration:(NSTimeInterval)duration;
// 设置延迟几秒开始动画,默认为0
+ (void)setAnimationDelay:(NSTimeInterval)delay;
// 设置动画开始的时间。默认是现在,即立即执行。
+ (void)setAnimationStartDate:(NSDate *)startDate;
// 设置动画运动曲线,控制动画速度,是枚举类型UIViewAnimationCurve,默认是UIViewAnimationCurveEaseInOut
/**
typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
UIViewAnimationCurveEaseInOut, // 慢进慢出,中间速度快
UIViewAnimationCurveEaseIn, // 开始慢,由慢而快
UIViewAnimationCurveEaseOut, // 结束时慢,由快而慢
UIViewAnimationCurveLinear, // 线性匀速
};
*/
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve;
// 设置动画重复次数,默认为零
+ (void)setAnimationRepeatCount:(float)repeatCount;
// 是否使用自动返回到原始状态的动画,当repeat count 不为零时才有作用,默认为NO
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;
// 设置如果开始一个动画正在播放的时候,另外一个动画开始了,是否在上一个动画现在的状态(还未完成状态)开始下一个动画,
// 这样会是一个动画“堆积”的过程,默认是只当上一个动画结束动画状态后再开始新的动画,默认为NO
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;
// 设置View的转场动画方向,枚举类型UIViewAnimationTransition
/**
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
UIViewAnimationTransitionNone, // 无转场动画
UIViewAnimationTransitionFlipFromLeft, // 从左翻转
UIViewAnimationTransitionFlipFromRight, // 从右翻转
UIViewAnimationTransitionCurlUp, // 从下向上
UIViewAnimationTransitionCurlDown, // 从上向下
};
*/
// cache :是否使用试图缓存
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;
// 是否允许动画,默认是yes
+ (void)setAnimationsEnabled:(BOOL)enabled;
// 代码块内执行无动画变换,他与-setAnimationEnabled:的区别在于前者使整个首尾之间均无动画,后者可以针对个别变换使之无动画。
+ (void)performWithoutAnimation:(void (NS_NOESCAPE ^)(void))actionsWithoutAnimation NS_AVAILABLE_IOS(7_0);
// 9.0后新添加方法,获取父类持续时间。
+ (NSTimeInterval)inheritedAnimationDuration NS_AVAILABLE_IOS(9_0);
e.g:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
squareView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
squareView.backgroundColor = [UIColor redColor];
[self.view addSubview:squareView];
UIButton * topButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 600, 100, 50)];
[topButton addTarget:self action:@selector(topButtonAction:) forControlEvents:(UIControlEventTouchUpInside)];
topButton.backgroundColor = [UIColor redColor];
[topButton setTitle:@"首尾式动画" forState:(UIControlStateNormal)];
[self.view addSubview:topButton];
}
- (void)topButtonAction:(UIButton *)sender
{
[UIView beginAnimations:@"oneAnimation" context:@"I am Pass Context"];
[UIView setAnimationDuration:3.0f];
// [UIView setAnimationsEnabled:NO];
[UIView setAnimationDelegate:self];
[UIView setAnimationWillStartSelector:@selector(animationWillStart: context:)];
[UIView setAnimationDelay:1.0f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationRepeatCount:2];
[UIView setAnimationRepeatAutoreverses:YES];
// [UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:squareView cache:NO];
[UIView setAnimationDidStopSelector:@selector(animationDidStop: finished: context:)];
squareView.alpha = 0.5;
squareView.backgroundColor = [UIColor blueColor];
CGAffineTransform transform_0 = CGAffineTransformMakeTranslation(200, 0);
CGAffineTransform transform_1 = CGAffineTransformMakeRotation(M_PI);
CGAffineTransform transform_all = CGAffineTransformConcat(transform_1, transform_0);
squareView.transform = transform_all;
[UIView commitAnimations];
}
-(void)animationWillStart:(NSString *)animationID context:(void *)context
{
NSLog(@"animationID = %@, context = %@", animationID, context);
}
-(void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
NSLog(@"animationID = %@, finished = %@, context = %@", animationID, finished, context);
}
在这里特别注意的是:当设置了-setAnimationsEnabled:为NO时,动画将消失,同时代理方法将只会运行已经停止的方法(-(void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
),而无法检测将要开始的方法(-(void)animationWillStart:(NSString *)animationID context:(void *)context
)
二、block动画(UIViewAnimationWithBlocks)
在View动画中最常用的还是block动画,相对于首尾式动画来说可以简单的操作动画完成后的事物。
// 参数:duration-持续时间、delay-延迟时间、options-动画类型,其中options为枚举类型UIViewAnimationOptions:
/**
typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {
//常规动画属性设置(可以同时选择多个进行)
UIViewAnimationOptionLayoutSubviews = 1 << 0, // 动画过程中保证子视图跟随一起运动
UIViewAnimationOptionAllowUserInteraction = 1 << 1, // 动画过程中允许用户交互
UIViewAnimationOptionBeginFromCurrentState = 1 << 2, //视图从当前状态开始
UIViewAnimationOptionRepeat = 1 << 3, // 重复动画
UIViewAnimationOptionAutoreverse = 1 << 4, // 动画运行到结束点后仍然以动画方式回到初始点
UIViewAnimationOptionOverrideInheritedDuration = 1 << 5, // 忽略嵌套的持续时间
UIViewAnimationOptionOverrideInheritedCurve = 1 << 6, // 忽略嵌套的曲线设置
UIViewAnimationOptionAllowAnimatedContent = 1 << 7, // 动画过程中重绘视图 (仅限转场动画)
UIViewAnimationOptionShowHideTransitionViews = 1 << 8, // 视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅限转场动画)
UIViewAnimationOptionOverrideInheritedOptions = 1 << 9, // 不继承任何父动画设置动画、类型类型等
// 动画曲线速度控制-与首尾式曲线设置一样(只可从其中选择一个)
UIViewAnimationOptionCurveEaseInOut = 0 << 16, // default
UIViewAnimationOptionCurveEaseIn = 1 << 16,
UIViewAnimationOptionCurveEaseOut = 2 << 16,
UIViewAnimationOptionCurveLinear = 3 << 16,
// 转场动画设置(仅适用于转场动画设置,可以从中选择一个进行设置)
UIViewAnimationOptionTransitionNone = 0 << 20, // default,无转场动画
UIViewAnimationOptionTransitionFlipFromLeft = 1 << 20, // 从左向右翻转
UIViewAnimationOptionTransitionFlipFromRight = 2 << 20, // 从右向左翻转
UIViewAnimationOptionTransitionCurlUp = 3 << 20, // 从下向上翻页(不是翻转!!!)
UIViewAnimationOptionTransitionCurlDown = 4 << 20,// 从上向下翻页
UIViewAnimationOptionTransitionCrossDissolve = 5 << 20, // 溶解显示新视图
UIViewAnimationOptionTransitionFlipFromTop = 6 << 20,// 从上向下翻转
UIViewAnimationOptionTransitionFlipFromBottom = 7 << 20,// 从下向上翻转
} NS_ENUM_AVAILABLE_IOS(4_0);
*/
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);
// 与上面一个方法相比将延迟时间设为了0,动画效果固定为UIViewAnimationOptionLayoutSubviews | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionNone
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // delay = 0.0, options = 0
// 将完成后的block给去掉了
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations NS_AVAILABLE_IOS(4_0); // delay = 0.0, options = 0, completion = NULL
#pragma ---------以上是普通动画方法------------
//相对于普通方法多了一个阻尼(dampingRatio),范围是(0-1),同时多了一个初始弹簧速度(一般是0);
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
#pragma ---------------以上是弹簧动画方法-----------------
// 普通转场动画,针对某一个指定的视图
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);
// 该方法是将toView添加到fromView的父视图中的动画效果。结束后fromView会从它的父视图上被移除
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // toView added to fromView.superview, fromView removed from its superview
#pragma -----------------以上是转场动画方法-------------------
// 在一个或者一组视图上执行系统规定的动画,在这里的系统规定是指开始时间、延时时间、持续时间等属性会与系统默认的相同,执行效果就是模糊化消失。当动画结束后这一组视图会在其父视图中被移除!
+ (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray<__kindof UIView *> *)views options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))parallelAnimations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
e.g:
@interface ViewAnimationViewController ()
{
UIView * squareView;
UIView * transformView;
}
@end
@implementation ViewAnimationViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
squareView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
squareView.backgroundColor = [UIColor redColor];
[self.view addSubview:squareView];
transformView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
transformView.backgroundColor = [UIColor orangeColor];
NSArray * titleArray = @[@"普通block动画", @"弹簧block动画", @"转场block动画", @"系统block动画"];
for (int i = 0; i < titleArray.count; i++) {
UIButton * topButton = [[UIButton alloc] initWithFrame:CGRectMake(12+(i*90), 500, 80, 30)];
[topButton addTarget:self action:@selector(topButtonAction:) forControlEvents:(UIControlEventTouchUpInside)];
topButton.tag = 10000 + i;
topButton.titleLabel.font = [UIFont systemFontOfSize:12];
topButton.backgroundColor = [UIColor redColor];
[topButton setTitle:titleArray[i] forState:(UIControlStateNormal)];
[self.view addSubview:topButton];
}
}
- (void)topButtonAction:(UIButton *)sender
{
if (sender.tag == 10000) {
[UIView animateWithDuration:2.0 delay:0 options:UIViewAnimationOptionLayoutSubviews | UIViewAnimationOptionAutoreverse |UIViewAnimationCurveEaseIn | UIViewAnimationOptionTransitionNone animations:^{
squareView.backgroundColor = [UIColor blueColor];
CGAffineTransform transform_0 = CGAffineTransformMakeTranslation(150, 0);
CGAffineTransform transform_1 = CGAffineTransformMakeRotation(M_PI);
CGAffineTransform transform_all = CGAffineTransformConcat(transform_1, transform_0);
squareView.transform = transform_all;
} completion:^(BOOL finished) {
[UIView animateWithDuration:2.0 animations:^{
squareView.frame = CGRectMake(100, 400, 100, 100);
}];
}];
}
if (sender.tag == 10001) {
[UIView animateWithDuration:2.0 delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:10 options:0 animations:^{
squareView.frame = CGRectMake(100, 200, 100, 100);
} completion:^(BOOL finished) {
}];
}
if (sender.tag == 10002) {
[UIView transitionFromView:squareView toView:transformView duration:2.0 options:UIViewAnimationOptionTransitionCrossDissolve completion:^(BOOL finished) {
[UIView transitionWithView:transformView duration:2.0 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
transformView.backgroundColor = [UIColor purpleColor];
} completion:^(BOOL finished) {
}];
}];
}
if (sender.tag == 10003) {
[UIView performSystemAnimation:UISystemAnimationDelete onViews:@[transformView] options:UIViewAnimationOptionTransitionCurlDown animations:^{
transformView.transform = CGAffineTransformMakeTranslation(-150, 0);
} completion:^(BOOL finished) {
NSLog(@"%@", NSStringFromClass([transformView.superview class]));
NSLog(@"%@", [transformView.superview class]);
}];
}
}
三、帧动画(UIViewKeyframeAnimations)
运用场景:帧动画常用于同一个视图的分段动画,和用于不同视图的组合动画。
// 除了参数options外其他与普通block动画大抵相同,其中options是枚举类型UIViewKeyframeAnimationOptions
/*
typedef NS_OPTIONS(NSUInteger, UIViewKeyframeAnimationOptions) {
UIViewKeyframeAnimationOptionLayoutSubviews = UIViewAnimationOptionLayoutSubviews,
UIViewKeyframeAnimationOptionAllowUserInteraction = UIViewAnimationOptionAllowUserInteraction,
UIViewKeyframeAnimationOptionBeginFromCurrentState = UIViewAnimationOptionBeginFromCurrentState,
UIViewKeyframeAnimationOptionRepeat = UIViewAnimationOptionRepeat,
UIViewKeyframeAnimationOptionAutoreverse = UIViewAnimationOptionAutoreverse,
UIViewKeyframeAnimationOptionOverrideInheritedDuration = UIViewAnimationOptionOverrideInheritedDuration,
UIViewKeyframeAnimationOptionOverrideInheritedOptions = UIViewAnimationOptionOverrideInheritedOptions,
#pragma -----------以上与UIViewAnimationOptions中的一部分对应,在此不多赘述。
UIViewKeyframeAnimationOptionCalculationModeLinear = 0 << 10, // default,线性的计算模式,按顺序连续变换。
UIViewKeyframeAnimationOptionCalculationModeDiscrete = 1 << 10, // 离散的计算模式,分散的点式变换。
UIViewKeyframeAnimationOptionCalculationModePaced = 2 << 10,// 均匀执行计算模式,不管你添加帧动画时如何设置的起始时间和持续时间,都会在持续的总时间内均匀执行动画。
UIViewKeyframeAnimationOptionCalculationModeCubic = 3 << 10, // 这个从效果上看好像和线性的差不多~
UIViewKeyframeAnimationOptionCalculationModeCubicPaced = 4 << 10 // 这个就是上面两个的组合体~~
} NS_ENUM_AVAILABLE_IOS(7_0);
*/
// 创建关键帧域的帧动画
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
// 添加关键帧动画 ,起始时间和持续时间值均在0.0和1.0之间,是指以关键帧域所设置的持续时间为对比的相对时间。e.g:frameStartTime若为0,则从第零秒开始,若为0.2,则从总持续时间的五分之一处开始。同理frameDuration若为0.2 则该帧动画持续时间占总持续时间的五分之一。
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations NS_AVAILABLE_IOS(7_0);
e.g:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
squareView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
squareView.backgroundColor = [UIColor redColor];
[self.view addSubview:squareView];
UIButton * topButton = [[UIButton alloc] initWithFrame:CGRectMake(200, 500, 80, 30)];
[topButton addTarget:self action:@selector(topButtonAction:) forControlEvents:(UIControlEventTouchUpInside)];
topButton.titleLabel.font = [UIFont systemFontOfSize:12];
topButton.backgroundColor = [UIColor redColor];
[topButton setTitle:@"view帧动画" forState:(UIControlStateNormal)];
[self.view addSubview:topButton];
}
- (void)topButtonAction:(UIButton *)sender
{
[UIView animateKeyframesWithDuration:10.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.25 animations:^{
squareView.backgroundColor = [UIColor yellowColor];
squareView.transform = CGAffineTransformMakeScale(2.0, 2.0);
}];
[UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.25 animations:^{
squareView.backgroundColor = [UIColor blueColor];
squareView.transform = CGAffineTransformMakeTranslation(100, 0);
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{
squareView.backgroundColor = [UIColor purpleColor];
squareView.transform = CGAffineTransformMakeRotation(M_PI);
}];
[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
squareView.backgroundColor = [UIColor cyanColor];
squareView.transform = CGAffineTransformMakeScale(0.5, 0.5);
}];
} completion:^(BOOL finished) {
}];
}
四、补充:UIImageView的序列帧动画
UIImageView有一组属性方法可以播放一组图片,类似于GIF播放效果。---其实可以完全被GIF取代~~
@property (nullable, nonatomic, copy) NSArray *animationImages;
@property (nullable, nonatomic, copy) NSArray *highlightedAnimationImages NS_AVAILABLE_IOS(3_0);
//一个周期持续时间. 默认是图片数量*1/30秒,即每秒播放30张图片(30fps)
@property (nonatomic) NSTimeInterval animationDuration;
@property (nonatomic) NSInteger animationRepeatCount; // 默认为0,无限循环
- (void)startAnimating;// 开始动画
- (void)stopAnimating;// 结束动画
- (BOOL)isAnimating; // 判断是否处于动画状态。
在此就不举例了~