實現iOS 9 Task Switcher動畫
阿新 • • 發佈:2018-12-27
升級到iOS 9以後,發現新的task switcher的動畫蠻有趣的,於是就動手實現了下,最終效果如下~
思路
- 首先我們需要一個橫向的scroll view,可以用UICollectionView,也可以自己實現一個。scroll view裡每一頁都是一張card,一屏5張card:
| |
|card card card card card|
| |
- 其次,我們需要在scrollViewDidScroll中判斷每張card距離中心的距離,根據這個值來調整它的alpha,scale以及x軸的translation。
alpha:右邊的card alpha都是1,左邊的越靠左alpha越小
scale: 從左往右依次變大
translation:除了中間的card,所有的card都會右偏,而為了讓中間card大部分都露出來,右邊的card偏移需要比左邊大
開工
1. 橫向滾動的scroll view
在scrollViewDidScroll中,我們提供一個delegate方法,告訴使用者每一頁距離中心的位置,以便apply各種transform到這個view上,delegate方法如下:
@protocol InfiniteScrollViewDelegate <NSObject> - (void)updateView:(UIView *)view withProgress:(CGFloat)progress scrollDirection:(ScrollDirection)direction; @end
說明一下progress的含義,如果一屏有5個visible views的話,那麼它的值會從-2變化到2:
| |
|-2...-1...0...1...2|
| |
2. 根據每一頁的位置來設定它的transform
首先是alpha,中心右邊的card alpha都是1,而左邊的會越來越淡,所以我們可以這樣寫:
if (progress >= 0) {
view.alpha = 1;
} else {
view.alpha = 1 - fabs(progress) * 0.2;
}
其次是scale,由左往右依次變大:
CGAffineTransform transform = CGAffineTransformIdentity;
CGFloat scale = 1 + (progress) * 0.03;
transform = CGAffineTransformScale(transform, scale, scale);
最後是x軸的translation,除了中間的card,所有的card都會往右偏,而為了讓中間card大部分都露出來,右邊的card偏移需要比左邊大
CGFloat translation = 0;
if (progress > 0) {
translation = fabs(progress) * SCREEN_WIDTH / 2.2;
} else {
translation = fabs(progress) * SCREEN_WIDTH / 15;
}
transform = CGAffineTransformTranslate(transform, translation, 0);
完整的實現:
- (void)updateView:(UIView *)view withProgress:(CGFloat)progress scrollDirection:(ScrollDirection)direction
{
// adjust z-index of each views
NSMutableArray *views = [[self.scrollView allViews] mutableCopy];
[views sortUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2) {
return view1.tag > view2.tag;
}];
for (UIView *view in views) {
[view.superview bringSubviewToFront:view];
}
// alpha
if (progress >= 0) {
view.alpha = 1;
} else {
view.alpha = 1 - fabs(progress) * 0.2;
}
CGAffineTransform transform = CGAffineTransformIdentity;
// scale
CGFloat scale = 1 + (progress) * 0.03;
transform = CGAffineTransformScale(transform, scale, scale);
// translation
CGFloat translation = 0;
if (progress > 0) {
translation = fabs(progress) * SCREEN_WIDTH / 2.2;
} else {
translation = fabs(progress) * SCREEN_WIDTH / 15;
}
transform = CGAffineTransformTranslate(transform, translation, 0);
view.transform = transform;
}