在上一篇文章中,我們已經(jīng)能用JazzHands做出一個(gè)完整的 Guide 動(dòng)畫了,但是這個(gè)動(dòng)畫還有一些不足.
- 它是用 Frame 來(lái)布局的,在不同的機(jī)型上適配起來(lái)很麻煩
JazzHands 2.0已經(jīng)解決了這個(gè)問(wèn)題
用 AutoLayout 來(lái)布局,用 IFTTTConstraintConstantAnimation 和 IFTTTConstraintMultiplierAnimation來(lái)做動(dòng)畫
使用起來(lái)代碼會(huì)復(fù)雜一些,但是對(duì)屏幕適配,屏幕旋轉(zhuǎn), iPad split-screen 支持的更好.
使用 IFTTTAnimatedPagingScrollViewController
IFTTTAnimatedPagingScrollViewController
內(nèi)部Override 下面這2個(gè)方法,結(jié)合 AutoLayout 完成適配.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration:
接下來(lái)我們使用AutoLayout 和 JazzHands做一個(gè)簡(jiǎn)單的 透明度漸變動(dòng)畫.
創(chuàng)建一個(gè)空 ViewController 繼承 IFTTTAnimatedPagingScrollViewController
這是 JazzHands 提供給我們的方便結(jié)合使用 AutoLayout 制作動(dòng)畫的類,以后我們使用 AutoLayout 做動(dòng)畫直接繼承這個(gè)類就可以了,它已經(jīng)幫我們初始化好了 scrollView,contentView,Animator
在類擴(kuò)展中只需添加我們動(dòng)畫的 View
@interface JazzHandsPageViewControllerDemo ()
@property (strong,nonatomic) UIView *v;
@end
在 ViewDidLoad 中
- (void)viewDidLoad {
[super viewDidLoad];
self.v=[UIView new];
self.v.backgroundColor=[UIColor redColor];
[self.contentView addSubview:self.v]; //注意添加 view到 contentView 中
self.scrollView.showsHorizontalScrollIndicator=YES;
self.scrollView.backgroundColor=[UIColor whiteColor];
self.edgesForExtendedLayout = UIRectEdgeNone; //ios7以后 NavigationBar 會(huì)遮住內(nèi)容,加上這行保證內(nèi)容顯示在 navigationBar 下面,一般來(lái)說(shuō)我們的 Guide 動(dòng)畫都是全屏的,不需要加這行
[self.v mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.equalTo(@30); //固定 view 寬高為30
}];
[self keepView:self.v onPage:0]; //keepView 下面會(huì)說(shuō)到
IFTTTAlphaAnimation *alpha=[IFTTTAlphaAnimation animationWithView:self.v];
[self.animator addAnimation:alpha];
[alpha addKeyframeForTime:0 alpha:1];
[alpha addKeyframeForTime:1 alpha:0]; //注意 time 是 1
}
運(yùn)行一下,效果如圖
上面的代碼和我們前2篇文章使用 Frame 布局制作動(dòng)畫類似,但有3點(diǎn)不同.
- 將 View 添加到了 contentView中.
- Masonry 給 view 布局
- addKeyframeForTime:1 time 是1.
關(guān)于第一點(diǎn) : 將 View 添加到了 contentView中.
引用一段 斯坦福大學(xué)公開課:iOS 7應(yīng)用開發(fā)

gif 中手機(jī)屏幕是全屏的 scrollView, 后面的圖片是 contentView, 我們將contentView 用scrollView.addsubView(contentView)
, 添加到 scrollView 中,再將其他所有需要展示的 View 添加到 contentView 中, scrollView 是為后面的 ContentView提供一個(gè)窗口
我們手指滑動(dòng)屏幕, scrollView.contentOffset.x
改變,我們能看到的 contentView 的內(nèi)容看起來(lái)跟著滾動(dòng)變化了,但實(shí)際上 contentView 的位置是固定的.
我們?cè)诶^承 IFTTTAnimatedPagingScrollViewController
時(shí)contentView,scrollView
已經(jīng)被初始化好了,我們只需將需要展示的 view 添加到 self.contentView
中即可
關(guān)于第二點(diǎn)
既然我們使用 AutoLayout, 那么對(duì) view 進(jìn)行布局,設(shè)置動(dòng)畫,就都要使用 AutoLayout, 這里使用 Mansonry 開源庫(kù)來(lái)設(shè)置約束.
關(guān)于第三點(diǎn) : addKeyframeForTime:1 time 是1.
之前我們使用 Frame
//創(chuàng)建一個(gè)透明度動(dòng)畫
IFTTTAlphaAnimation *alpha=[IFTTTAlphaAnimation animationWithView:self.v];
//將所有動(dòng)畫添加到 animator 中
[self.animator addAnimation:alpha];
//添加2個(gè)幀,在 scrollView 從 0 滾動(dòng)到 200 時(shí), 淡出我們的 View
[alpha addKeyframeForTime:0 alpha:1.0];
[alpha addKeyframeForTime:200 alpha:0.0];
//scrollView 代理
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[self.animator animate:scrollView.contentOffset.x];
}
}
現(xiàn)在我們使用 AutoLayout 和 IFTTTAnimatedPagingScrollViewController
IFTTTAlphaAnimation *alpha=[IFTTTAlphaAnimation animationWithView:self.v];
[self.animator addAnimation:alpha];
[alpha addKeyframeForTime:0 alpha:1];
[alpha addKeyframeForTime:1 alpha:0];
IFTTTAnimatedPagingScrollViewController
內(nèi)部幫我們做了scrollView.contentOffset.x
和time
的轉(zhuǎn)換
具體實(shí)現(xiàn)類似 :
time = self.scrollView.contentOffset.x / self.pageWidth;
它將 contentOffset.x轉(zhuǎn)換為 頁(yè)面寬度的百分比,
[alpha addKeyframeForTime:0 alpha:1];
[alpha addKeyframeForTime:1 alpha:0];
所以這段代碼我們?cè)O(shè)置的動(dòng)畫是, scrollView 從第一頁(yè)滾動(dòng)到第二頁(yè), view 的透明度從1變化到0
[keepView:onPage:]方法
如果你細(xì)心的話,可以發(fā)現(xiàn)上面的例子中,我們并沒(méi)有設(shè)置 x 軸的約束,但是紅色方塊的位置在 X 軸居中,
就是因?yàn)?code>[self keepView:self.v onPage:0];,這行代碼
我們寫一個(gè)例子來(lái)看看這個(gè)方法及其重載方法的作用,
新建一個(gè)空白 ViewController, 繼承自 IFTTTAnimatedPagingScrollViewController
類擴(kuò)展中
@interface JazzHandsKeepViewDemo ()
@property (strong,nonatomic) UIImageView *ifttt;
@end
ViewDidLoad 中
- (void)viewDidLoad {
[super viewDidLoad];
self.ifttt=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"IFTTTPresents"]];
self.scrollView.backgroundColor=[UIColor greenColor];
self.scrollView.showsHorizontalScrollIndicator=YES;
[self.contentView addSubview:self.ifttt];
self.edgesForExtendedLayout = UIRectEdgeNone;
[self keepView:self.ifttt onPage:0];
}
我們還需要 Override 一個(gè)方法,告訴父類我們需要的頁(yè)數(shù)
-(NSUInteger)numberOfPages{
return 4;
}
運(yùn)行一下,效果如圖
我們并沒(méi)有給 ITFFF imageView設(shè)置任何的動(dòng)畫和約束,但是它在 X軸居中,
就是因?yàn)槲覀冊(cè)O(shè)置了[self keepView:self.ifttt onPage:0];
,讓 imageView 出現(xiàn)在第一頁(yè),
我們繼續(xù)使用它的重載方法..
[self keepView:self.ifttt onPages:@[@(0),@(1)]];
運(yùn)行結(jié)果如下..
注意看下面的 scrollIndicator, 即使我滑動(dòng)到第二頁(yè),它還是保持在屏幕中間的位置,
繼續(xù)修改代碼為
[self keepView:self.ifttt onPages:@[@(0),@(1)] atTimes:@[@(0),@(1)]];
這個(gè)運(yùn)行的結(jié)果和上面一樣,其實(shí)我們調(diào)用 [self keepView:self.ifttt onPages:@[@(0),@(1)]];
時(shí),
內(nèi)部會(huì)調(diào)用它的重載方法
[self keepView:self.ifttt onPages:@[@(0),@(1)] atTimes:@[@(0),@(1)]];
,
所以這2個(gè)方法的效果是一樣的
再次修改代碼,最后一次了...
[self keepView:self.ifttt onPages:@[@(0),@(1.5)] atTimes:@[@(0),@(1)]];
運(yùn)行結(jié)果如下
這個(gè)運(yùn)行結(jié)果看起來(lái)有點(diǎn)詭異,
先來(lái)解釋這個(gè)方法,
[self keepView:self.ifttt onPages:@[@(0),@(1)] atTimes:@[@(0),@(1)]];
調(diào)用這個(gè)方法起到的效果是, ImageView 會(huì)保持在第一頁(yè)和第二頁(yè)的中間,
當(dāng)我們調(diào)用這個(gè)方法時(shí),內(nèi)部會(huì)給 imageView 和 contentView 添加一個(gè) X 軸的約束,讓 imageView 保持在頁(yè)面中間,
onPages:@[@(0),@(1)]
就代表保持在第一二頁(yè)的中間, atTimes:@[@(0),@(1)]
代表2個(gè)時(shí)間點(diǎn),
它內(nèi)部的實(shí)現(xiàn)是在 scrollView 滾動(dòng)時(shí),將 scrollView.contentOffset.x 累加給 imageView 和 contentView 的約束的 constant 上,也就是 scrollView 滾動(dòng)多少, imageView 也滾動(dòng)多少,所以產(chǎn)生的效果就是 imageView 一直保持在第一頁(yè)和第二頁(yè)的中間不動(dòng).
別忘了 JazzHands 是一個(gè)幀動(dòng)畫,所以我們?cè)O(shè)置參數(shù)的解釋就是,在 time=0時(shí), imageView 保持在第一頁(yè)中間, time=1時(shí), imageView 保持在第二頁(yè)中間,我們只需設(shè)置這2個(gè)關(guān)鍵幀,其余的 JazzHands 會(huì)幫我們搞定.
那么下面這個(gè)方法呢?..
[self keepView:self.ifttt onPages:@[@(0),@(1.5)] atTimes:@[@(0),@(1)]];
在 time=0時(shí), imageView 保持在第一頁(yè)中間,在 time=1時(shí), imageView 保持在第二頁(yè)中間 再偏離中間0.5倍的位置.
具體的實(shí)現(xiàn)是,現(xiàn)在 scrollView 滾動(dòng)10px, imageView 滾動(dòng) 15px, 所以從第一頁(yè)滾動(dòng)到第二頁(yè)的過(guò)程中,感覺(jué) imageView 滾動(dòng)的距離比較長(zhǎng),滾動(dòng)的比較快.
所以[self keepView:self.ifttt onPages:@[@(0),@(1.5)] atTimes:@[@(0),@(1)]];
這個(gè)方法不僅能將 view 保持在某一頁(yè)面出現(xiàn),調(diào)整參數(shù)后,還能制作 X 軸偏移的動(dòng)畫,所以后我們給 view設(shè)置約束時(shí),不用設(shè)置 X 軸的約束,只需調(diào)用此方法即可約束 view 的 X 軸位置.
明白了這點(diǎn)就可以無(wú)壓力的制作動(dòng)畫了..
Done
所有代碼你可以在 Github 中找到
下一篇 我們用 JazzHands+AutoLayout 來(lái)模仿 官方 Demo
的效果