早在2017年年初,我就用七八篇文章的篇幅系统介绍过Objective-C中的CoreAnimation框架。CoreAnimation是iOS中实现动画的框架,整个iOS中的动画(比如UIView中封装的动画、UIViewController切换时的转场动画、UITableViewCell移除增添时的动画等,都是对CoreAnimation的封装)都是通过CoreAnimation实现的。
UIView动画
@interface UIView(UIViewAnimationWithBlocks)
+ (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)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)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)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
@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);
@end
上面的代码展示了UIView动画的相关接口,它能够实现我们日常开发中80%以上的动画效果,可以处理frame、alpha、transform等,但是UIView的内置动画是不能自定义中间状态的,也就是说,不能实现关键帧动画。这些UIView内置动画,实际上都是对CoreAnimation动画的封装。
实现一个自定义弹窗视图
我们平时在开发的时候,经常会遇到这样的需求:在页面中弹出一个自定义视图,除了弹出的自定义视图之外,页面的其他位置都置灰态。比如下图这样:
要实现这样的效果,基本思路是:
1,新建一个弹窗背景视图,背景颜色设置为黑色,透明度设置为0.5;
2,将自定义弹窗视图加在弹窗背景视图上;
3,触发弹出弹窗视图的时候,将弹窗背景视图添加到程序的根窗口上;
4,移除弹窗视图的时候,就将弹窗背景视图从父视图上移除即可。
代码如下:
//NormanHudView.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface NormanHudView : UIView
/**
灰色背景上方的展示视图。该视图为用户自定义。
required
*/
@property (nonatomic, strong)UIView *customView;
/**
决定hud视图是否展示的开关
required
*/
@property (nonatomic, assign)BOOL showCustomView;
@end
NS_ASSUME_NONNULL_END
//NormanHudView.m
#import "NormanHudView.h"
@interface NormanHudView()
@end
@implementation NormanHudView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColorcolorWithWhite:0 alpha:0.5];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:selfaction:@selector(tapAction:)];
[self addGestureRecognizer:tap];
}
return self;
}
#pragma mark - tapAction
- (void)tapAction:(UIGestureRecognizer *)recognizer
{
CGPoint tapPoint = [recognizer locationInView:self];
if (CGRectContainsPoint(self.customView.frame, tapPoint)) {
return;//点击点在自定义视图范围之外才会使提示视图消失~
}
self.showCustomView = NO;
}
#pragma mark - setter && Getter
- (void)setShowCustomView:(BOOL)showCustomView
{
if (showCustomView) {
[[UIApplication sharedApplication].keyWindowaddSubview:self];
//这里是展示customView的时候的动画,可根据自己的情况自定义
[UIView animateWithDuration:2.f delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.customView.alpha = 1;
} completion:^(BOOL finished) {
NSLog(@"completion");
}];
} else {
[self removeFromSuperview];
}
}
- (void)setCustomView:(UIView *)customView
{
_customView = customView;
[self addSubview:_customView];
_customView.center = self.center;
_customView.alpha = 0;
}
@end
上述代码是我封装的一个浮层视图。
展示浮层上的自定义提示视图的时候,我使用了UIView的动画,可以在这里实现浮层上的自定义提示视图展示时候的动画。
给浮层的背景视图添加一个点击手势,以在点击的时候移除该浮层。
写在最后
UIKit框架中各组件自带的各种动画效果,实际上都是对CoreAnimation这个框架中相关接口的封装。
通过UIView的系统封装好的动画,我们可以实现日常开发中80%的动画需求,剩下的一些较复杂的动画,可以使用CoreAnimation来自定义。
关于CoreAnimation,我之前写过一系列的文章,在这里罗列一下:
其实,除了上面提到的相关动画,还有一个动画相关的类是有必要跟大家提一下的,那就是CAEmitterLayer(粒子发射器)。QQ点赞效果、红包雨、下雪、火焰等等都是通过该类来实现的。可以参考这篇文章:
https://www.jianshu.com/p/c54ffd7412e7
除了上述原生动画,有一个开源动画项目也是经常使用的,那就是Lottie。Lottie是Airbnb开源的一个面向iOS、Android、React Native的动画库,能分析Adobe After Effects导出的动画,并且能让原生App像使用静态素材一样使用这些动画,完美实现动画效果。
设计师设计出一组动画效果,然后导成JSON文件,我使用Lottie解析该JSON文件就可以将动画效果展示出来,使用非常简单。