如何在视图之间实现滑动/滑动动画?

use*_*136 27 swipe ios

我有一些想要在iOS程序中滑动的视图.现在,我正在使用模态样式在它们之间滑动,并使用交叉溶解动画.但是,我希望有一个像你在主屏幕上看到的滑动/滑动动画等.我不知道如何编写这样的转换,并且动画样式不是可用的模态转换样式.谁能给我一个代码示例?它不需要是模态模型或任何东西,我只是发现最简单.

Rob*_*Rob 36

从iOS 7开始,如果要为两个视图控制器之间的过渡设置动画,则可以使用自定义过渡,如WWDC 2013视频自定义过渡使用视图控制器中所述.例如,要自定义新视图控制器的显示,您将:

  1. 目标视图控制器将指定self.modalPresentationStyletransitioningDelegate为演示动画:

    - (instancetype)initWithCoder:(NSCoder *)coder {
        self = [super initWithCoder:coder];
        if (self) {
            self.modalPresentationStyle = UIModalPresentationCustom;
            self.transitioningDelegate = self;
        }
        return self;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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];
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)
  4. 您还将实现一个表示控制器,负责清理视图层次结构.在这种情况下,由于我们完全覆盖了呈现视图,因此我们可以在完成转换时将其从层次结构中删除:

    @interface PresentationController: UIPresentationController
    @end
    
    @implementation PresentationController
    
    - (BOOL)shouldRemovePresentersView {
        return true;
    }
    
    @end
    
    Run Code Online (Sandbox Code Playgroud)
  5. 或者,如果您希望此手势具有交互性,您还可以:

    • 创建交互控制器(通常为a UIPercentDrivenInteractiveTransition);

    • 你有没有UIViewControllerAnimatedTransitioning实现interactionControllerForPresentation,显然会返回上述交互控制器;

    • 有一个更新的手势(或你有什么) interactionController

这在上述使用视图控制器的自定义转换中进行了描述.

例如,自定义导航控制器推/弹,请参阅导航控制器自定义过渡动画


下面,请查找原始答案的副本,该答案早于自定义过渡.


@ sooper的答案是正确的,CATransition可以产生你正在寻找的效果.

但是,顺便说一句,如果你的背景不是白色的,那么在转换结束kCATransitionPushCATransition会有一个奇怪的淡入淡出和淡出可能会分散注意力(当在图像之间导航时,尤其是它会使它略微闪烁的效果) .如果你受此影响,我发现这个简单的转换非常优雅:你可以准备你的"下一个视图"就在屏幕右边,然后动画当前视图从屏幕向左移动同时你同时动画动画下一个视图以移动到当前视图的位置.请注意,在我的示例中,我在单个视图控制器中将主视图中的子视图设置为动画,但您可能会想到:

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")
                 }];
Run Code Online (Sandbox Code Playgroud)

很明显,你必须调整这个以便你如何设置你的控件,但这会产生一个非常简单的过渡,没有褪色或类似的东西,只是一个很好的平滑过渡.

需要注意的是:首先,这个例子和CATransition示例都不像SpringBoard主屏幕动画(你谈过),这是连续的(即如果你在滑动的一半,你可以停下来回去或者随你).这些转变是曾经发起它们的转变,它们恰好发生了.如果您需要实时交互,也可以这样做,但它是不同的.

更新:

如果你想使用跟踪用户手指的连续手势,你可以使用UIPanGestureRecognizer而不是UISwipeGestureRecognizer,我认为animateWithDurationCATransition那种情况更好.我修改了我handlePanGesture改变frame坐标以与用户的手势协调,然后我修改了上面的代码,以便在用户放手时完成动画.效果很好.我认为你不能CATransition轻易做到这一点.

例如,您可以在控制器的主视图上创建一个手势处理程序:

[self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]];
Run Code Online (Sandbox Code Playgroud)

处理程序可能看起来像:

- (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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这使用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);
}
Run Code Online (Sandbox Code Playgroud)

你的具体实现无疑会有所不同,但希望这说明了这个想法.

已经说明了所有这些(补充和澄清这个旧答案),我应该指出我不再使用这种技术了.如今,我通常使用UIScrollView(打开"分页")或(在iOS 6中)a UIPageViewController.这使您无法编写此类手势处理程序(并享受滚动条,弹跳等额外功能).在UIScrollView实现中,我只是回应scrollViewDidScroll事件以确保我懒得加载必要的子视图.


soo*_*per 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"];
Run Code Online (Sandbox Code Playgroud)


Leo*_*uli 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;
Run Code Online (Sandbox Code Playgroud)

然后UIPageControl用来获得这些点.:)