2012-05-01 90 views
27

我有一些我想在iOS程序中滑動的視圖。現在,我用一種模式化的風格在他們之間滑動,並使用交叉消解動畫。但是,我想要在主屏幕上看到類似的滑動/滑動動畫。我不知道如何編寫這樣一個轉變和動畫的風格是不是可用的模式過渡的風格。任何人都可以給我一個代碼的例子嗎?它並不需要是一個模態模型或任何東西,我只是發現,最容易的。如何在視圖之間實現滑動/滑動動畫?

+0

請注意,你可以平凡使用iOS「分頁」 - 完整教程:stackoverflow.com/a/26024779/294884 – Fattie

+0

如果你需要一個真正的自定義過渡,完整的教程2018年https://stackoverflow.com/a/48081504/294884 – Fattie

回答

9

您可以創建一個CATransition動畫。這裏有一個如何,你可以在第二視圖(左起),同時推動當前視圖滑出到畫面的例子:

UIView *theParentView = [self.view superview]; 

CATransition *animation = [CATransition animation]; 
[animation setDuration:0.3]; 
[animation setType:kCATransitionPush]; 
[animation setSubtype:kCATransitionFromLeft]; 
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; 

[theParentView addSubview:yourSecondViewController.view]; 
[self.view removeFromSuperview]; 

[[theParentView layer] addAnimation:animation forKey:@"showSecondViewController"]; 
36

由於iOS的7,如果你想動畫兩種顯示控制器之間的過渡,你會使用自定義的轉換,在WWDC 2013的視頻Custom Transitions Using View Controllers討論。例如,定製你將一個新的視圖控制器的演示:

  1. 目標視圖控制器會指定self.modalPresentationStyletransitioningDelegate爲演示動畫:

    - (instancetype)initWithCoder:(NSCoder *)coder { 
        self = [super initWithCoder:coder]; 
        if (self) { 
         self.modalPresentationStyle = UIModalPresentationCustom; 
         self.transitioningDelegate = self; 
        } 
        return self; 
    } 
    
  2. 此委託(在此例如,視圖控制器本身)將符合UIViewControllerTransitioningDelegate和實施:

    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented 
                        presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { 
        return [[PresentAnimator alloc] init]; 
    } 
    
    // in iOS 8 and later, you'd also specify a presentation controller 
    
    - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source { 
        return [[PresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting]; 
    } 
    
  3. 你會實現,將執行所需動畫的動畫師:

    @interface PresentAnimator : NSObject <UIViewControllerAnimatedTransitioning> 
    
    @end 
    
    @implementation PresentAnimator 
    
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { 
        return 0.5; 
    } 
    
    // do whatever animation you want below 
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { 
        UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 
        UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; 
    
        [[transitionContext containerView] addSubview:toViewController.view]; 
        CGFloat width = fromViewController.view.frame.size.width; 
        CGRect originalFrame = fromViewController.view.frame; 
        CGRect rightFrame = originalFrame; rightFrame.origin.x += width; 
        CGRect leftFrame = originalFrame; leftFrame.origin.x -= width/2.0; 
        toViewController.view.frame = rightFrame; 
    
        toViewController.view.layer.shadowColor = [[UIColor blackColor] CGColor]; 
        toViewController.view.layer.shadowRadius = 10.0; 
        toViewController.view.layer.shadowOpacity = 0.5; 
    
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ 
         fromViewController.view.frame = leftFrame; 
         toViewController.view.frame = originalFrame; 
         toViewController.view.layer.shadowOpacity = 0.5; 
        } completion:^(BOOL finished) { 
         [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 
        }]; 
    } 
    
    @end 
    
  4. 你也想實現一個演示控制器,負責清理視圖層次對你的照顧。在這種情況下,因爲我們完全覆蓋的呈現來看,我們可以從層次結構中刪除它時,轉換完成:

    @interface PresentationController: UIPresentationController 
    @end 
    
    @implementation PresentationController 
    
    - (BOOL)shouldRemovePresentersView { 
        return true; 
    } 
    
    @end 
    
  5. 可選,如果你想這個手勢是互動的,你也將:

    • 創建交互控制器(通常爲UIPercentDrivenInteractiveTransition);

    • 你的UIViewControllerAnimatedTransitioning也執行interactionControllerForPresentation,這顯然會返回上述交互控制器;

    • 有一個手勢(或你有什麼),更新的interactionController

這在上述Custom Transitions Using View Controllers所有描述。

例如自定義導航控制器推/流行的,見Navigation controller custom transition animation


下面,請找我原來的答覆,這早自定義切換的副本。


@ sooper的回答是正確的,CATransition可以產生你正在尋找的效果。

但是,順便說一下,如果你的背景不是白色的CATransitionkCATransitionPush有一個奇怪的淡入淡出,並在過渡期結束,可以分散注意力(圖像間導航時淡出,尤其是,它借它有點閃爍的效果)。如果你遭受這種困境,我發現這個簡單的過渡非常優雅:你可以準備好你的「下一個視圖」,使其僅在屏幕右側顯示,然後在屏幕左側移動當前視圖的動畫爲下一個視圖添加動畫,以移至當前視圖所在的位置。請注意,在我的例子,我進出的單一視圖控制器內的主視圖的動畫子視圖,但你可能得到的想法:

float width = self.view.frame.size.width; 
float height = self.view.frame.size.height; 

// my nextView hasn't been added to the main view yet, so set the frame to be off-screen 

[nextView setFrame:CGRectMake(width, 0.0, width, height)]; 

// then add it to the main view 

[self.view addSubview:nextView]; 

// now animate moving the current view off to the left while the next view is moved into place 

[UIView animateWithDuration:0.33f 
         delay:0.0f 
        options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction 
       animations:^{ 
        [nextView setFrame:currView.frame]; 
        [currView setFrame:CGRectMake(-width, 0.0, width, height)]; 
       } 
       completion:^(BOOL finished){ 
        // do whatever post processing you want (such as resetting what is "current" and what is "next") 
       }]; 

很顯然,你必須調整這個對於你爲什麼已經控制了所有的設置,但是這會產生一個非常簡單的過渡,不會退色或類似的事情,只是一個很好的平滑過渡。

需要注意的是:首先,這個例子和CATransition的例子都不像SpringBoard的主屏幕動畫(你提到的那個),它是連續的(即如果你通過滑動一半,你可以停止並返回或其他)。這些轉變是曾經啓動它們的轉變,它們只是發生了。如果您需要實時交互,也可以完成,但它不同。

更新:

如果要使用連續的手勢跟蹤用戶的手指,你可以使用UIPanGestureRecognizer而不是UISwipeGestureRecognizer,我覺得animateWithDuration比在這種情況下CATransition更好。我修改了我的handlePanGesture以更改frame座標以與用戶的手勢協調,然後我修改了上述代碼,以便在用戶放開時完成動畫。工作得很好。我不認爲你可以很容易地用CATransition這樣做。

例如,你可能會在控制器的主視圖中創建一個手勢處理機:

[self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]]; 

而且處理程序可能看起來像:

- (void)handlePan:(UIPanGestureRecognizer *)gesture 
{ 
    // transform the three views by the amount of the x translation 

    CGPoint translate = [gesture translationInView:gesture.view]; 
    translate.y = 0.0; // I'm just doing horizontal scrolling 

    prevView.frame = [self frameForPreviousViewWithTranslate:translate]; 
    currView.frame = [self frameForCurrentViewWithTranslate:translate]; 
    nextView.frame = [self frameForNextViewWithTranslate:translate]; 

    // if we're done with gesture, animate frames to new locations 

    if (gesture.state == UIGestureRecognizerStateCancelled || 
     gesture.state == UIGestureRecognizerStateEnded || 
     gesture.state == UIGestureRecognizerStateFailed) 
    { 
     // figure out if we've moved (or flicked) more than 50% the way across 

     CGPoint velocity = [gesture velocityInView:gesture.view]; 
     if (translate.x > 0.0 && (translate.x + velocity.x * 0.25) > (gesture.view.bounds.size.width/2.0) && prevView) 
     { 
      // moving right (and/or flicked right) 

      [UIView animateWithDuration:0.25 
            delay:0.0 
           options:UIViewAnimationOptionCurveEaseOut 
          animations:^{ 
           prevView.frame = [self frameForCurrentViewWithTranslate:CGPointZero]; 
           currView.frame = [self frameForNextViewWithTranslate:CGPointZero]; 
          } 
          completion:^(BOOL finished) { 
           // do whatever you want upon completion to reflect that everything has slid to the right 

           // this redefines "next" to be the old "current", 
           // "current" to be the old "previous", and recycles 
           // the old "next" to be the new "previous" (you'd presumably. 
           // want to update the content for the new "previous" to reflect whatever should be there 

           UIView *tempView = nextView; 
           nextView = currView; 
           currView = prevView; 
           prevView = tempView; 
           prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero]; 
          }]; 
     } 
     else if (translate.x < 0.0 && (translate.x + velocity.x * 0.25) < -(gesture.view.frame.size.width/2.0) && nextView) 
     { 
      // moving left (and/or flicked left) 

      [UIView animateWithDuration:0.25 
            delay:0.0 
           options:UIViewAnimationOptionCurveEaseOut 
          animations:^{ 
           nextView.frame = [self frameForCurrentViewWithTranslate:CGPointZero]; 
           currView.frame = [self frameForPreviousViewWithTranslate:CGPointZero]; 
          } 
          completion:^(BOOL finished) { 
           // do whatever you want upon completion to reflect that everything has slid to the left 

           // this redefines "previous" to be the old "current", 
           // "current" to be the old "next", and recycles 
           // the old "previous" to be the new "next". (You'd presumably. 
           // want to update the content for the new "next" to reflect whatever should be there 

           UIView *tempView = prevView; 
           prevView = currView; 
           currView = nextView; 
           nextView = tempView; 
           nextView.frame = [self frameForNextViewWithTranslate:CGPointZero]; 
          }]; 
     } 
     else 
     { 
      // return to original location 

      [UIView animateWithDuration:0.25 
            delay:0.0 
           options:UIViewAnimationOptionCurveEaseOut 
          animations:^{ 
           prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero]; 
           currView.frame = [self frameForCurrentViewWithTranslate:CGPointZero]; 
           nextView.frame = [self frameForNextViewWithTranslate:CGPointZero]; 
          } 
          completion:NULL]; 
     } 
    } 
} 

使用這些簡單的frame方法,您會大概定義爲您所需的UX:

- (CGRect)frameForPreviousViewWithTranslate:(CGPoint)translate 
{ 
    return CGRectMake(-self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height); 
} 

- (CGRect)frameForCurrentViewWithTranslate:(CGPoint)translate 
{ 
    return CGRectMake(translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height); 
} 

- (CGRect)frameForNextViewWithTranslate:(CGPoint)translate 
{ 
    return CGRectMake(self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height); 
} 

您的部分特別的實施無疑會有所不同,但希望這可以說明這個想法。

說明了所有這些(補充和澄清這個舊的答案),我應該指出,我不再使用這種技術。現在,我通常使用UIScrollView(打開「分頁」)或(在iOS 6中)UIPageViewController。這使您擺脫了編寫這種手勢處理程序的業務(並享受諸如滾動條,彈跳等額外功能)。在UIScrollView實現中,我只是對scrollViewDidScroll事件做出響應以確保我正在延遲加載必要的子視圖。

+0

嘿@Rob,很好地完成了,就像我所做的一樣。我真的想讓UIPanGestureRecognizer很好地工作。任何機會的一段代碼,特別是與動畫?很多! – StinkyCat

+0

特定控制器內子視圖之間的轉換。 – StinkyCat

+0

@StinkyCat當我第一次寫這個,我手動添加子視圖移動他們的窗口等我後來喜歡一些更簡單的方法解決方案,即'UIPageViewController'(iOS 6.0及以上,如果你想滑動視圖)或「UIScrollView」(在其中打開分頁,然後響應「scrollViewWillScroll」委託方法來確定將哪些視圖添加爲子視圖。這些方法很好,因爲它可以讓您擺脫創建自己的手勢識別器,你會得到滾動條或頁面計數器等等 – Rob

8

如果您希望切換頁面時具有與跳板相同的頁面滾動/滑動效果,那麼爲什麼不使用UIScrollView

CGFloat width = 320; 
CGFloat height = 400; 
NSInteger pages = 3; 

UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,width,height)]; 
scrollView.contentSize = CGSizeMake(width*pages, height); 
scrollView.pagingEnabled = YES; 

然後用UIPageControl來得到這些點。 :)