動(dòng)畫的世界豐富多彩,令人陶醉
隨著工作的深入,其實(shí)每個(gè) App 中,最關(guān)鍵的莫過于用戶交互,隨著技術(shù)時(shí)代的發(fā)展,人們已經(jīng)不再滿足于手指點(diǎn)點(diǎn)碰碰就完成功能。用戶更加期待炫酷的操作體驗(yàn)。需要眼前一亮的感覺。這些都是 App 將要解決的問題。而動(dòng)畫交互無疑是最多的一種手段。但是,現(xiàn)在也不是簡(jiǎn)單動(dòng)畫的天下,需要更多的創(chuàng)意,繁雜而簡(jiǎn)潔的創(chuàng)意。
動(dòng)畫效果的UIView Object

結(jié)構(gòu)圖
// 顯示動(dòng)畫
- (void)showWithDuration:(CGFloat)duration animated:(BOOL)animated;
// 隱藏動(dòng)畫
- (void)hideWithDuration:(CGFloat)duration animated:(BOOL)animated;
// 創(chuàng)建view
- (void)buildView;
// 動(dòng)畫百分比(手動(dòng)設(shè)置動(dòng)畫的程度)
- (void)percent:(CGFloat)percent;
制定統(tǒng)一的動(dòng)畫接口
即:相關(guān)有動(dòng)畫效果的類,有同一的動(dòng)畫方法命名
- 為了實(shí)現(xiàn)后續(xù)復(fù)雜的動(dòng)畫組合
- 后續(xù)的代碼維護(hù)極為方便
- 優(yōu)先考慮里氏代換原則
動(dòng)畫中的高內(nèi)聚低耦合原理
- 高內(nèi)聚:有動(dòng)畫效果的類,自身具有動(dòng)畫方法
- 不要把實(shí)現(xiàn)動(dòng)畫的細(xì)節(jié)暴露在外
- 設(shè)計(jì)動(dòng)畫類盡量要符合單一職能原則,以便后續(xù)方便組合成復(fù)雜的動(dòng)畫效果
設(shè)計(jì)動(dòng)畫函數(shù)的注意事項(xiàng)
- 動(dòng)畫方法的命名統(tǒng)一
- 預(yù)留非動(dòng)畫情形的設(shè)計(jì)(tableView等reuse情況)
- 用百分比來表示動(dòng)畫的執(zhí)行程度
- 懶加載的使用
動(dòng)畫效果就是frame值的重新設(shè)定
[UIView animateWithDuration:duration animations:^{
self.frame = self.midRect;
self.alpha = 1.f;
}];
一個(gè)具有動(dòng)畫效果的類
// TranslateView.h
#import <UIKit/UIKit.h>
@interface TranslateView : UIView
//** 顯示動(dòng)畫 */
- (void)show;
/** 隱藏動(dòng)畫 */
- (void)hide;
/** 創(chuàng)建view */
- (void)buildView;
/** 動(dòng)畫百分比(手動(dòng)設(shè)置動(dòng)畫的程度) */
- (void)percent:(CGFloat)percent;
@end
// TranslateView.m
#import "TranslateView.h"
@interface TranslateView ()
@property (nonatomic) CGFloat offsetY;
@property (nonatomic) CGRect startRect;
@property (nonatomic) CGRect midRect;
@property (nonatomic) CGRect endRect;
@property (nonatomic) CGRect curRect;
@end
@implementation TranslateView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.alpha = 0.f;
self.offsetY = 20.0f;
self.backgroundColor = [UIColor blackColor];
[self buildView];
}
return self;
}
// 創(chuàng)建view
- (void)buildView {
self.startRect = self.frame;
self.midRect = CGRectMake(self.startRect.origin.x,
self.startRect.origin.y + self.offsetY,
self.startRect.size.width,
self.startRect.size.height);
self.endRect = CGRectMake(self.startRect.origin.x,
self.startRect.origin.y + self.offsetY * 2,
self.startRect.size.width,
self.startRect.size.height);
}
// 顯示動(dòng)畫
- (void)showWithDuration:(CGFloat)duration animated:(BOOL)animated {
if (animated == YES) {
[UIView animateWithDuration:duration animations:^{
self.frame = self.midRect;
self.alpha = 1.f;
self.curRect = self.frame;
}];
} else {
self.frame = self.midRect;
self.alpha = 1.f;
self.curRect = self.frame;
}
}
// 隱藏動(dòng)畫
- (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;
}
}
// 動(dòng)畫百分比(手動(dòng)設(shè)置動(dòng)畫的程度)
- (void)percent:(CGFloat)percent {
CGFloat tmpOffsetY = 0;
CGFloat stateHeight = 20;
if (percent <= 0) {
tmpOffsetY = percent * self.offsetY;
} else if (percent >= 1) {
tmpOffsetY = self.offsetY;
} else {
tmpOffsetY = percent * self.offsetY;
}
if (CGRectGetMinY(self.curRect) + tmpOffsetY < stateHeight || CGRectGetMaxY(self.curRect) + tmpOffsetY > CGRectGetHeight([UIScreen mainScreen].bounds)) {
return;
}
self.frame = CGRectMake(self.curRect.origin.x,
self.curRect.origin.y + tmpOffsetY,
self.curRect.size.width,
self.curRect.size.height);
self.curRect = self.frame;
}
- (void)show
{
[self showWithDuration:2.0f animated:YES];
}
- (void)hide
{
[self hideWithDuration:0.5f animated:YES];
}
@end
里氏代換原則處理動(dòng)畫類的繼承問題
SourceView *tmpView = [[ChildTwoView alloc] init];
[tmpView show];
- 里氏代換原則的基本原理 (多態(tài))
- 設(shè)計(jì)中要確保父類可以直接調(diào)用子類的方法
- 將父類設(shè)計(jì)成虛類
什么是多態(tài)
1.定義
某一類事物的多種表現(xiàn)形態(tài)
舉例說明:
1)生活中:動(dòng)物:貓—波斯貓
2)程序中:父類指針指向子類對(duì)象
2.條件
- 子類繼承父類
- 子類有重寫父類的方法
- 父類指針指向子類對(duì)象
動(dòng)物 a = [貓 alloc]init];
動(dòng)物 a = [狗 alloc]init];
表現(xiàn)形式:在父類指針指向不同的對(duì)象的時(shí)候,通過父類指針調(diào)用被重寫的方法,會(huì)執(zhí)行該指針?biāo)赶虻哪莻€(gè)對(duì)象的方法;
3.實(shí)現(xiàn)
@interface Computer : NSObject
- (void)system;
@end
@interface PC : Computer
// 重寫system方法
@end
@interface Mac : Computer
// 重寫system方法
@end
Computer *com = nil;
Computer = [PC alloc]init]; //實(shí)例化PC對(duì)象
[PC system];
Computer = [Mac alloc]init; //實(shí)例化Mac對(duì)象
[Mac system];
4.原理
- id類型:通用對(duì)象指針類型,弱類型,編譯的時(shí)候不進(jìn)行具體的類型檢查。
- 動(dòng)態(tài)綁定:動(dòng)態(tài)類型可以做到在程序直到執(zhí)行時(shí)才確定對(duì)象的真實(shí)類型,進(jìn)而確定需要調(diào)用那個(gè)對(duì)象方法。
ObjC 不同于其他程序設(shè)計(jì)語言,它可以在運(yùn)行的時(shí)候加入新的數(shù)據(jù)類型和新的程序模塊,動(dòng)態(tài)類型識(shí)別,動(dòng)態(tài)綁定,動(dòng)態(tài)加載。
5.優(yōu)點(diǎn):
- 多態(tài)最重要的優(yōu)點(diǎn)在于簡(jiǎn)化了編程接口,何以理解?它允許在類和類之間重用一些習(xí)慣性的命名,而不是為每一個(gè)新加的函數(shù)命名一個(gè)新的名字。這樣,編程接口就是一些抽象行為的集合,從而和實(shí)現(xiàn)接口的類區(qū)分開。
- 多態(tài)也使得代碼可以分散在不同的對(duì)象中,而不用視圖在一個(gè)函數(shù)中考慮到所有可能的對(duì)象。這樣可以優(yōu)化代碼的擴(kuò)展性和復(fù)用性。當(dāng)一個(gè)新的情景出現(xiàn)時(shí),無需對(duì)現(xiàn)有的代碼進(jìn)行改動(dòng),而只需要增加一個(gè)新的類和新的同名的方法。
詳細(xì)參考
動(dòng)畫中的模塊化設(shè)計(jì)
- 動(dòng)畫效果實(shí)現(xiàn)難度的判斷
- 將看到的動(dòng)畫效果拆分成小模塊
- 將寫好的小模塊組合成你所需要的動(dòng)畫效果
//ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 復(fù)雜的動(dòng)畫被寫進(jìn)了BaseAnimationView當(dāng)中,沒有暴露不必要的細(xì)節(jié)
BaseAnimationView *baseView = [[BaseAnimationView alloc] initWithFrame:CGRectZero];
[self.view addSubview:baseView];
[baseView show];
}
//BaseAnimationView.m
- (void)show
{
[self.translateView show];
}
- (void)hide
{
[self.translateView hide];
}
- (void)buildView
{
self.translateView = [[TranslateView alloc] initWithFrame:CGRectMake(50, 100, 2, 50)];
[self addSubview:self.translateView];
}
- (void)percent:(CGFloat)percent
{
[self.translateView percent:percent];
}
延時(shí)執(zhí)行某方法
[self performSelector:@selector(excuteAfterDelay) withObject:nil afterDelay:6];
初始化UIView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// ......
}
return self;
}