設(shè)計(jì)復(fù)雜的iOS動畫效果

動畫的世界豐富多彩,令人陶醉

隨著工作的深入正压,其實(shí)每個 App 中,最關(guān)鍵的莫過于用戶交互责球,隨著技術(shù)時代的發(fā)展焦履,人們已經(jīng)不再滿足于手指點(diǎn)點(diǎn)碰碰就完成功能。用戶更加期待炫酷的操作體驗(yàn)雏逾。需要眼前一亮的感覺嘉裤。這些都是 App 將要解決的問題。而動畫交互無疑是最多的一種手段栖博。但是屑宠,現(xiàn)在也不是簡單動畫的天下,需要更多的創(chuàng)意仇让,繁雜而簡潔的創(chuàng)意典奉。

動畫效果的UIView Object

結(jié)構(gòu)圖
// 顯示動畫
- (void)showWithDuration:(CGFloat)duration animated:(BOOL)animated;

// 隱藏動畫
- (void)hideWithDuration:(CGFloat)duration animated:(BOOL)animated;

// 創(chuàng)建view
- (void)buildView;

// 動畫百分比(手動設(shè)置動畫的程度)
- (void)percent:(CGFloat)percent;

制定統(tǒng)一的動畫接口

即:相關(guān)有動畫效果的類,有同一的動畫方法命名

  • 為了實(shí)現(xiàn)后續(xù)復(fù)雜的動畫組合
  • 后續(xù)的代碼維護(hù)極為方便
  • 優(yōu)先考慮里氏代換原則

動畫中的高內(nèi)聚低耦合原理

  • 高內(nèi)聚:有動畫效果的類丧叽,自身具有動畫方法
  • 不要把實(shí)現(xiàn)動畫的細(xì)節(jié)暴露在外
  • 設(shè)計(jì)動畫類盡量要符合單一職能原則卫玖,以便后續(xù)方便組合成復(fù)雜的動畫效果

設(shè)計(jì)動畫函數(shù)的注意事項(xiàng)

  • 動畫方法的命名統(tǒng)一
  • 預(yù)留非動畫情形的設(shè)計(jì)(tableView等reuse情況)
  • 用百分比來表示動畫的執(zhí)行程度
  • 懶加載的使用

動畫效果就是frame值的重新設(shè)定

[UIView animateWithDuration:duration animations:^{
        self.frame = self.midRect;
        self.alpha = 1.f;
    }];

一個具有動畫效果的類

//  TranslateView.h
#import <UIKit/UIKit.h>

@interface TranslateView : UIView

//** 顯示動畫 */
- (void)show;

/** 隱藏動畫 */
- (void)hide;

/** 創(chuàng)建view */
- (void)buildView;

/** 動畫百分比(手動設(shè)置動畫的程度) */
- (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);
}


// 顯示動畫
- (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;
    }
}

// 隱藏動畫
- (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;
    }
}



// 動畫百分比(手動設(shè)置動畫的程度)
- (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

里氏代換原則處理動畫類的繼承問題

SourceView *tmpView = [[ChildTwoView alloc] init];
[tmpView show];
  • 里氏代換原則的基本原理 (多態(tài))
  • 設(shè)計(jì)中要確保父類可以直接調(diào)用子類的方法
  • 將父類設(shè)計(jì)成虛類

什么是多態(tài)

1.定義

某一類事物的多種表現(xiàn)形態(tài)

舉例說明:

1)生活中:動物:貓—波斯貓

2)程序中:父類指針指向子類對象

2.條件

  • 子類繼承父類
  • 子類有重寫父類的方法
  • 父類指針指向子類對象
動物 a = [貓 alloc]init];
動物 a = [狗 alloc]init];

表現(xiàn)形式:在父類指針指向不同的對象的時候,通過父類指針調(diào)用被重寫的方法踊淳,會執(zhí)行該指針?biāo)赶虻哪莻€對象的方法假瞬;

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對象
[PC system];

Computer = [Mac alloc]init; //實(shí)例化Mac對象
[Mac system];

4.原理

  • id類型:通用對象指針類型迂尝,弱類型脱茉,編譯的時候不進(jìn)行具體的類型檢查。
  • 動態(tài)綁定:動態(tài)類型可以做到在程序直到執(zhí)行時才確定對象的真實(shí)類型雹舀,進(jìn)而確定需要調(diào)用那個對象方法芦劣。

ObjC 不同于其他程序設(shè)計(jì)語言,它可以在運(yùn)行的時候加入新的數(shù)據(jù)類型和新的程序模塊说榆,動態(tài)類型識別虚吟,動態(tài)綁定寸认,動態(tài)加載。

5.優(yōu)點(diǎn):

  • 多態(tài)最重要的優(yōu)點(diǎn)在于簡化了編程接口串慰,何以理解?它允許在類和類之間重用一些習(xí)慣性的命名偏塞,而不是為每一個新加的函數(shù)命名一個新的名字。這樣邦鲫,編程接口就是一些抽象行為的集合灸叼,從而和實(shí)現(xiàn)接口的類區(qū)分開。
  • 多態(tài)也使得代碼可以分散在不同的對象中庆捺,而不用視圖在一個函數(shù)中考慮到所有可能的對象古今。這樣可以優(yōu)化代碼的擴(kuò)展性和復(fù)用性。當(dāng)一個新的情景出現(xiàn)時滔以,無需對現(xiàn)有的代碼進(jìn)行改動捉腥,而只需要增加一個新的類和新的同名的方法。

詳細(xì)參考

Objective-C 繼承和多態(tài)

動畫中的模塊化設(shè)計(jì)

  • 動畫效果實(shí)現(xiàn)難度的判斷
  • 將看到的動畫效果拆分成小模塊
  • 將寫好的小模塊組合成你所需要的動畫效果
//ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // 復(fù)雜的動畫被寫進(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];
}

延時執(zhí)行某方法

[self performSelector:@selector(excuteAfterDelay) withObject:nil afterDelay:6];

初始化UIView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // ......
    }
    return self;
}

Demo on Github

原文閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抵碟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坏匪,更是在濱河造成了極大的恐慌拟逮,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件适滓,死亡現(xiàn)場離奇詭異敦迄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)凭迹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門颅崩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蕊苗,你說我怎么就攤上這事沿后。” “怎么了朽砰?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵尖滚,是天一觀的道長。 經(jīng)常有香客問我瞧柔,道長漆弄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任造锅,我火速辦了婚禮撼唾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哥蔚。我一直安慰自己倒谷,他們只是感情好蛛蒙,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著渤愁,像睡著了一般牵祟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抖格,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天诺苹,我揣著相機(jī)與錄音,去河邊找鬼雹拄。 笑死收奔,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的滓玖。 我是一名探鬼主播筹淫,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呢撞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起饰剥,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤殊霞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汰蓉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绷蹲,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年顾孽,在試婚紗的時候發(fā)現(xiàn)自己被綠了祝钢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡若厚,死狀恐怖拦英,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情测秸,我是刑警寧澤疤估,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站霎冯,受9級特大地震影響铃拇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沈撞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一慷荔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缠俺,春花似錦显晶、人聲如沸贷岸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凰盔。三九已至,卻和暖如春倦春,著一層夾襖步出監(jiān)牢的瞬間户敬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工睁本, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尿庐,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓呢堰,卻偏偏與公主長得像抄瑟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枉疼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1.import static是Java 5增加的功能,就是將Import類中的靜態(tài)方法皮假,可以作為本類的靜態(tài)方法來...
    XLsn0w閱讀 1,212評論 0 2
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,125評論 29 470
  • 面向?qū)ο笾饕槍γ嫦蜻^程。 面向過程的基本單元是函數(shù)骂维。 什么是對象:EVERYTHING IS OBJECT(萬物...
    sinpi閱讀 1,045評論 0 4
  • 從小常常被父母教導(dǎo),人不可貌相潦刃,絕對不可以以貌取人侮措。但是現(xiàn)在人可以貌相的說法,我也是十分認(rèn)可乖杠。一個人的穿著...
    黃小蟻閱讀 759評論 0 1
  • 親愛的夏優(yōu)同學(xué): 很久沒見了是吧分扎,似乎沒想到我會在這個時候送你一份大禮吧,希望你能像我在去年收到你的禮物時有不一樣...
    徐徐思卿閱讀 298評論 0 4