UIView动画在平常的使用场景中比较普遍,UIView的属性改变时使用UIView动画过渡会比较自然,用起来也比较简单。
有UIViewAnimation、UIViewAnimationWithBlocks、UIViewKeyframeAnimations三种实现方式。
1、UIViewAnimation
下面是UIViewAnimation的基本调用方法(方法已做备注):
@interface UIView(UIViewAnimation)
//开始动画。传递的context值会传递给代理的start/did stop方法中。
+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;
//和beginAnimations方法成对出现。代表动画开始和执行,动画内容放在两个个方法中间。
+ (void)commitAnimations;
// 设置动画代理,当动画开始或者结束时会发消息给代理对象。默认是nil。
+ (void)setAnimationDelegate:(nullable id)delegate;
//动画即将开始时,执行selector,并且把beginAnimations:context:中传入的参数传进selector
+ (void)setAnimationWillStartSelector:(nullable SEL)selector;
//动画即将结束时,执行selector,并且把beginAnimations:context:中传入的参数传进selector
+ (void)setAnimationDidStopSelector:(nullable SEL)selector;
//动画的持续时间,以秒为单位。默认是0.2.
+ (void)setAnimationDuration:(NSTimeInterval)duration;
//动画延迟。默认是0。
+ (void)setAnimationDelay:(NSTimeInterval)delay;
//默认是now.
+ (void)setAnimationStartDate:(NSDate *)startDate;
//动画的节奏控制。默认是UIViewAnimationCurveEaseInOut。
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve;
//动画重复次数。默认是0.
+ (void)setAnimationRepeatCount:(float)repeatCount;
//动画是否逆向运行。如果是YES的话,动画会按照原来的运动轨迹逆向返回。默认是NO。
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;
//动画是否从当前状态开始。默认是NO。如果为YES,那么动画在运行过程中当前视图的位置将会作为新的动画的开始状态。如果设置为NO,新动画将使用视图最後状态的位置作为开始状态。
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;
//设置view的过渡效果, transition是设定过渡类型, cache为YES时代表使用视图缓存。
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;
//是否激活动画。如果是YES,就激活动画,当动画参数没有被激活那么动画属性的改变将被忽略。默认是YES。
+ (void)setAnimationsEnabled:(BOOL)enabled;
#if UIKIT_DEFINE_AS_PROPERTIES
@property(class, nonatomic, readonly) BOOL areAnimationsEnabled;
#else
+ (BOOL)areAnimationsEnabled;
#endif
+ (void)performWithoutAnimation:(void (NS_NOESCAPE ^)(void))actionsWithoutAnimation NS_AVAILABLE_IOS(7_0);
#if UIKIT_DEFINE_AS_PROPERTIES
@property(class, nonatomic, readonly) NSTimeInterval inheritedAnimationDuration NS_AVAILABLE_IOS(9_0);
#else
+ (NSTimeInterval)inheritedAnimationDuration NS_AVAILABLE_IOS(9_0);
#endif
@end
UIViewAnimation使用示例:
UIImage * image = [UIImage imageNamed:@"icon1"];
[imageView setImage:image];
//动画开始前视图状态
imageView.frame = CGRectMake(ScreenWidth/2-image.size.width/2, (ScreenHeight-image.size.height)/2,image.size.width,image.size.height);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0f];
image = [UIImage imageNamed:@"icon2"];
[imageView setImage:image];
//视图最终状态
imageView.frame = CGRectMake(ScreenWidth/2-25, ScreenHeight-50, 50, 50);
[UIView commitAnimations];
2、UIViewAnimationWithBlocks
UIViewAnimationWithBlocks的方法函数如下:
@interface UIView(UIViewAnimationWithBlocks)
/**
block动画,可以设置动画时长、动画延迟、动画选项、动画结束状态、动画完成后最终状态,
可根据自己实际需要选择下面3种实现方式
**/
+ (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);
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // delay = 0.0, options = 0
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations NS_AVAILABLE_IOS(4_0); // delay = 0.0, options = 0, completion = NULL
//视图过渡动画
+ (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);
//视图间过渡动画
+ (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
//弹簧动画
+ (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);
//删除视图动画
+ (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);
@end
主要分为简单的block动画、视图过渡动画、视图间过渡动画、弹簧动画和删除动画。
注:弹簧动画参数dampingRatio、velocity分别是阻尼大小、形变速度。
dampingRatio:实际指弹簧动画的摩擦力大小。值的范围是0.0~1.0。当阻尼大小越小代表阻力越小,反之则阻力越大,阻力越小弹簧动画的幅度就越大,越大弹簧幅度就越小,与现实中弹簧拉伸的道理是一样的。
velocity:指弹簧的变化的速度。速度越大,变化越快。
下面看下不同动画的代码示例:
(1)简单的block动画
-(void)AnimationWithSimpleBlocks{
UIImage * image = [UIImage imageNamed:@"heart"];
[imageView setImage:image];
imageView.frame = CGRectMake((ScreenWidth-image.size.width)/2, 150+(ScreenHeight-150-image.size.height)/2,image.size.width,image.size.height);
[UIView animateWithDuration:1.0f delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
//动画终点
imageView.frame = CGRectMake((ScreenWidth-image.size.width)/2, 150+(ScreenHeight-150-image.size.height)/2, image.size.width+20, image.size.height+20);
} completion:^(BOOL finished) {
//view最终状态(UIView会从动画结束状态转换到此处设定的状态,在转换过程中没有动画效果)
imageView.frame = CGRectMake((ScreenWidth-image.size.width)/2, 150+(ScreenHeight-150-image.size.height)/2,image.size.width,image.size.height);
}];
}
(2)视图过渡动画示例:
-(void)AnimationWithTransitionBlocks{
UIImage * image = [UIImage imageNamed:@"heart"];
[imageView setImage:image];
imageView.frame = CGRectMake(0, (ScreenHeight-image.size.height)/2,image.size.width,image.size.height);
//带有过渡效果
[UIView transitionWithView:imageView duration:1.5f options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
//动画终点
imageView.frame = CGRectMake((ScreenWidth-image.size.width)/2, 150+(ScreenHeight-150-image.size.height)/2, image.size.width+20, image.size.height+20);
} completion:^(BOOL finished) {
}];
}
(3)视图之间过渡动画代码示例:
-(void)AnimationBetweenViews{
UIView *view1 = [[UIView alloc]initWithFrame:self.view.frame];
view1.backgroundColor = [UIColor yellowColor];
UIView *view2 = [[UIView alloc]initWithFrame:self.view.frame];
view2.backgroundColor = [UIColor orangeColor];
[self.view addSubview:view1];
[self.view addSubview:view2];
[UIView transitionFromView:view1 toView:view2 duration:1.5f options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
[view1 removeFromSuperview];
[view2 removeFromSuperview];
}];
}
(4)弹簧动画示例
-(void)SpringAnimation{
UIImage * image = [UIImage imageNamed:@"heart"];
[imageView setImage:image];
imageView.frame = CGRectMake(0, 150+(ScreenHeight-150-image.size.height)/2,image.size.width,image.size.height);
[UIView animateWithDuration:1.0f delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0.5 options:0 animations:^{
//动画终点
imageView.frame = CGRectMake((ScreenWidth-image.size.width)/2, 150+(ScreenHeight-150-image.size.height)/2, image.size.width+20, image.size.height+20);
} completion:^(BOOL finished) {
}];
}
运行结果如下:
注:UIView的简单动画可以通过这两种方式来实现,如果是比较复杂的动画的话使用CAKeyframeAnimations实现会更方便。
3、CAKeyframeAnimations
CAKeyframeAnimations函数如下:
@interface UIView (UIViewKeyframeAnimations)
//设定动画时长、动画延迟、关键帧选项、关键帧
+ (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);
//添加关键帧动画的开始时间、帧动画在整个动画的比例
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations NS_AVAILABLE_IOS(7_0); // 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
@end
代码示例:
-(void)menuClick:(UIButton *)btn{
CGRect rect = CGRectMake(ScreenWidth/3-50, 275,0,0);
if (open) {
[UIView animateKeyframesWithDuration:duration delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
for (int i=0; i<iconArr.count; i++) {
[UIView addKeyframeWithRelativeStartTime:i*1.0/iconArr.count relativeDuration:1.0/iconArr.count animations:^{
UIImageView * functionBtn = (UIImageView *)[self.view viewWithTag:100+i];
functionBtn.frame = rect;
}];
}
open = NO;
} completion:nil];
}
else{
UIImage * icon1 = [UIImage imageNamed:@"functionIcon1"];
CGRect rect1 = CGRectMake(ScreenWidth/3, 130,50,50);
CGRect rect2 = CGRectMake(ScreenWidth/3+80, 210,50,50);
CGRect rect3 = CGRectMake(ScreenWidth/3+80, 290,50,50);
CGRect rect4 = CGRectMake(ScreenWidth/3, 370,50,50);
iconArr = @[icon1,icon1,icon1,icon1];
NSArray * rectArr = @[[NSValue valueWithCGRect:rect1],[NSValue valueWithCGRect:rect2],[NSValue valueWithCGRect:rect3],[NSValue valueWithCGRect:rect4]];
for (int i=0; i<4; i++) {
UIImageView * functionBtn = (UIImageView *)[self.view viewWithTag:100+i];
if (!functionBtn) {
functionBtn = [[UIImageView alloc]initWithImage:[iconArr objectAtIndex:i]];
functionBtn.tag = 100+i;
functionBtn.frame = rect;
[self.view addSubview:functionBtn];
}
}
[UIView animateKeyframesWithDuration:duration delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
for (int i=0; i<iconArr.count; i++) {
[UIView addKeyframeWithRelativeStartTime:i*1.0/iconArr.count relativeDuration:1.0/iconArr.count animations:^{
UIImageView * functionBtn = (UIImageView *)[self.view viewWithTag:100+i];
functionBtn.frame = [[rectArr objectAtIndex:i] CGRectValue];
}];
}
open = YES;
} completion:nil];
}
}
运行效果: