天天看點

ios開發——仿新版iBooks書本打開與關閉動畫

IOS新版iBooks吸引人的地方除了有幹淨整潔的界面、友善靈活的操作以及大容量的書籍容量以外。還有其優秀的使用者互動,尤其是其動畫的使用。打開一本書時書本緩慢放大并打開。關閉一本書後書本關閉并回到原位置。

如今我們來實作這個簡單的功能。

效果圖:

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的方法依據這個鍵取出值并進行推斷,應該也是能夠的。