IOS新版iBooks吸引人的地方除了有幹淨整潔的界面、友善靈活的操作以及大容量的書籍容量以外。還有其優秀的使用者互動,尤其是其動畫的使用。打開一本書時書本緩慢放大并打開。關閉一本書後書本關閉并回到原位置。
如今我們來實作這個簡單的功能。
效果圖:
用到的知識:
1、CAKeyframeAnimation的應用
2、怎樣在代理中區分兩個不同的動畫
3、坐标系轉換
思路:
這個動畫主要用到的是CAAnimation,而且是CAKeyframeAnimation。當使用者點選書本時。設定一個UIImageView(為其加上tag友善以後取)并将其放在選中書本的位置上(使用坐标系轉換)。接着通過動畫将其放大到全屏,完畢後将其錨點設定為(0,0.5)并讓其繞y軸選中π/2角度,将早已放在以下的textView(本app中是自己定義的readView)展示出來。
動畫完畢後将UIImageView的透明度設為0。這樣就是書本的打開過程。
關閉過程類似,依據tag取出UIImageView并将其旋轉,然後設定frame到原來書本的位置(能夠用私有變量記錄該位置),最後removeFromSuperview就可以。
代碼:
首先是“準備階段”
//BookReadView是展示textView和頂部底部附加view的自己定義view
CYZBookItemView *itemView = [notification object];
CYZBookReadView *readView = [[CYZBookReadView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, self.view.height) ItemView:itemView];
//設定delegate是為了當使用者從閱讀界面傳回時做出響應
readView.delegate = self;
//先将其alpha設為0防止使用者剛點選書本動畫還在進行中時就出現這一界面
readView.alpha = 0.0f;
[self.view addSubview:readView];
//展示動畫的UIImageView
UIImageView *showView = [[UIImageView alloc] initWithImage:itemView.bookImage.currentBackgroundImage];
//坐标系轉換
_itemPosition = [itemView.superview convertRect:itemView.frame toView:self.view];
showView.frame = _itemPosition;
//設定tag友善以後取出
showView.tag = 1001;
showView.backgroundColor = [UIColor clearColor];
[self.view addSubview:showView];
關于坐标系轉換(以矩形為例。point類似):
- (void)convertRect:toView:方法:
格式[被轉換者所在的View convertRect:被轉換者的frame或bounds toView:轉換到的View];傳回在目标View中的rect
比如,将目前視圖的子視圖viewA中的一個imageView轉換到目前視圖。
能夠例如以下調用:
[viewA convertRect:viewA.imageView.frame toRect:self.view];
或者[imageView.superView convertRect:imageView.frame toRect:self.view];
當中self指控制器。
- (void)convertRect:fromView:方法:
格式[要轉換到的View convertRect:被轉換者的frame或bounds fromView:被轉換者所在的View];傳回在目标View中的rect。
還是上一個樣例,能夠這樣寫:
[self.view convertRect:viewA.imageView.frame fromView:viewA];
或者[self.view convertRect:imageView.frame fromView:imageView.superView];
當中self指控制器
接着準備工作完畢了以後就能夠開始動畫了。
一下是打開動畫的部分
[UIView animateWithDuration:0.5f animations:^{
//将UIImageView放大為全屏
showView.frame = self.view.bounds;
} completion:^(BOOL finished) {
if (finished) {
//展示出readView
readView.alpha = 1.0f;
//設定錨點
showView.layer.anchorPoint = CGPointMake(0, 0.5);
#warning 不知道為什麼,不加以下這句話showView會顯示異常:即僅僅顯示一半
showView.frame = CGRectMake(0, 0, showView.width, showView.height);
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
//設定其繞y軸旋轉
animation.keyPath = @"transform.rotation.y";
//設定持續時間。能夠定義一常量來表示
animation.duration = 0.5f;
animation.speed = 0.55f;
animation.removedOnCompletion = NO;
//旋轉π/2度
[animation setValues:[NSArray arrayWithObjects:@0.0f, @-M_PI_2, nil]];
//設定代理便于回調
animation.delegate = self;
//這裡必須設定上key,為了接下來區分不同動畫
[showView.layer addAnimation:animation forKey:@"rotateToOpenBook"];
}
}];
代碼中凝視已經解釋得比較具體了,僅僅隻是正如warning寫得那樣。本來我是用showView.layer.transform來實作動畫的,這樣會省非常多事。比方不用在代理中區分動畫、不用記錄position來在方法間傳值等,隻是不知道為什麼showView總是僅僅顯示一半。求解釋╮(╯▽╰)╭
回調中:
[showView setAlpha:0.0f];
将其臨時隐藏。關上書時還要用它。
關上書的動畫大緻類似
#pragma mark - CYZBookViewDelegate
- (void)readViewDidBackWithItem:(CYZBookItemView *)item
{
//取出showView
UIImageView *showView = (UIImageView *)[self.view viewWithTag:1001];
//将其顯示出來
showView.alpha = 1.0f;
showView.layer.anchorPoint = CGPointMake(0, 0.5f);
showView.frame = CGRectMake(0, 0, showView.width, showView.height);
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"transform.rotation.y";
animation.duration = 0.5f;
animation.speed = 0.55f;
animation.removedOnCompletion = NO;
//反向旋轉
[animation setValues:[NSArray arrayWithObjects:@-M_PI_2, @0, nil]];
animation.delegate = self;
//設定不同的key為了區分動畫用
[showView.layer addAnimation:animation forKey:@"rotateToCloseBook"];
}
這裡要用一個不同的key來表示這個動畫。
該動畫的回調:
[UIView animateWithDuration:0.5f animations:^{
showView.frame = _itemPosition;
} completion:^(BOOL finished) {
[showView removeFromSuperview];
}];
_itemPosition是之前用來記錄轉換的坐标的私有變量,這幾行代碼的意思就是讓showView回到書本的位置然後令其消失。
這裡的關鍵問題在于。兩個動畫都用到了回調,怎樣區分呢?就用我們之前設定的key來區分:
回調的完整代碼:
#pragma mark - AnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
UIImageView *showView = (UIImageView *)[self.view viewWithTag:1001];
if (flag) {
if (showView && [[showView.layer animationForKey:@"rotateToOpenBook"] isEqual:anim]) {
[showView setAlpha:0.0f];
} else if (showView && [anim isEqual:[showView.layer animationForKey:@"rotateToCloseBook"]]) {
[UIView animateWithDuration:0.5f animations:^{
showView.frame = _itemPosition;
} completion:^(BOOL finished) {
[showView removeFromSuperview];
}];
}
}
}
通過showView所在層layer的方法 animationForKey:獲得自己設定的動畫,與代理中的anim做比較就可以區分。
另外。在stackOverflow上還看到一種方法:用kvc的方法為animation設值,并在代理中通過kvc的方法依據這個鍵取出值并進行推斷,應該也是能夠的。