动画效果的UIView Object
// 显示动画
- (void)showWithDuration:(CGFloat)duration animated:(BOOL)animated;
// 隐藏动画
- (void)hideWithDuration:(CGFloat)duration animated:(BOOL)animated;
// 创建view
- (void)buildView;
// 动画百分比(手动设置动画的程度)
- (void)percent:(CGFloat)percent;
制定统一的动画接口
即:相关有动画效果的类,有同一的动画方法命名
- 为了实现后续复杂的动画组合
- 后续的代码维护极为方便
- 优先考虑里氏代换原则
动画中的高内聚低耦合原理
- 高内聚:有动画效果的类,自身具有动画方法
- 不要把实现动画的细节暴露在外
- 设计动画类尽量要符合单一职能原则,以便后续方便组合成复杂的动画效果
设计动画函数的注意事项
- 动画方法的命名统一
- 预留非动画情形的设计(tableView等reuse情况)
- 用百分比来表示动画的执行程度
- 懒加载的使用
动画效果就是frame值的重新设定
[UIView animateWithDuration:duration animations:^{
self.frame = self.midRect;
self.alpha = 1.f;
}];
一个具有动画效果的类
#import "LineView.h"
@interface LineView ()
@property (nonatomic) CGRect startRect;
@property (nonatomic) CGRect midRect;
@property (nonatomic) CGRect endRect;
@end
@implementation LineView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.alpha = 0.f;
}
return self;
}
// 创建view
- (void)buildView {
// todo
self.startRect = self.frame;
self.midRect = CGRectMake(self.startRect.origin.x + self.offsetX,
self.startRect.origin.y,
self.startRect.size.width,
self.startRect.size.height);
self.endRect = CGRectMake(self.startRect.origin.x + self.offsetX * 2,
self.startRect.origin.y,
self.startRect.size.width,
self.startRect.size.height);
}
// 显示动画
- (void)showWithDuration:(CGFloat)duration animated:(BOOL)animated {
if (animated == YES) {
[UIView animateWithDuration:duration animations:^{
self.frame = self.midRect;
self.alpha = 1.f;
}];
} else {
self.frame = self.midRect;
self.alpha = 1.f;
}
}
// 隐藏动画
- (void)hideWithDuration:(CGFloat)duration animated:(BOOL)animated {
if (animated == YES) {
[UIView animateWithDuration:duration animations:^{
self.frame = self.endRect;
self.alpha = 0.f;
} completion:^(BOOL finished) {
self.frame = self.startRect;
}];
} else {
self.frame = self.startRect;
self.alpha = 0.f;
}
}
// 动画百分比(手动设置动画的程度)
- (void)percent:(CGFloat)percent {
CGFloat tmpOffsetX = 0;
if (percent <= 0) {
tmpOffsetX = 0;
} else if (percent >= 1) {
tmpOffsetX = self.offsetX;
} else {
tmpOffsetX = percent * self.offsetX;
}
self.frame = CGRectMake(self.startRect.origin.x + tmpOffsetX,
self.startRect.origin.y,
self.startRect.size.width,
self.startRect.size.height);
}
里氏代换原则处理动画类的继承问题
1 | SourceView *tmpView = [[ChildTwoView alloc] init]; |
- 里氏代换原则的基本原理 (多态)
- 设计中要确保父类可以直接调用子类的方法
- 将父类设计成虚类
动画中的模块化设计
- 动画效果实现难度的判断
- 将看到的动画效果拆分成小模块
- 将写好的小模块组合成你所需要的动画效果
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 复杂的动画被写进了BaseAnimationView当中,没有暴露不必要的细节
BaseAnimationView *baseView = [[BaseAnimationView alloc] initWithFrame:CGRectZero];
[self.view addSubview:baseView];
[baseView show];
}
BaseAnimation.m
- (void)show {
[self.circleView show];
[self.lineView show];
}
- (void)hide {
[self.circleView hide];
[self.lineView hide];
}
- (void)buildView {
self.circleView = [[CircleView alloc] initWithFrame:CGRectZero];
[self addSubview:self.circleView];
self.lineView = [[RectView alloc] initWithFrame:CGRectZero];
[self addSubview:self.lineView];
}
circleView.m / lineView.m
- (void)show {}
- (void)hide {}
- (void)buildView {}
延时执行某方法
[self performSelector:@selector(excuteAfterDelay) withObject:nil afterDelay:6];
初始化UIView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.rect = frame;
}
return self;
}