如何完成交互式 UIViewController 过渡?

Posted

技术标签:

【中文标题】如何完成交互式 UIViewController 过渡?【英文标题】:How to complete interactive UIViewController transition? 【发布时间】:2013-10-01 03:38:11 【问题描述】:

我一直在尝试使用新的 ios 7 自定义转换 API,并查看了我能找到的所有教程/文档,但我似乎无法为我的特定场景弄清楚这些东西。

所以基本上我要实现的是一个视图上的 UIPanGestureRecognizer,我将向上滑动并转换到一个 VC,该 VC 的视图将从底部向上滑动,而当我将手指向上拖动时,当前视图会向上滑动。

没有交互转换我没有问题,但是一旦我实现了交互(平移手势),我似乎无法完成转换。

下面是来自 VC 的符合 UIViewControllerTransitionDelegate 的相关代码,这是出售动画控制器所需的:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
    if ([segue.identifier isEqualToString:@"Swipe"]) 
        NSLog(@"PREPARE FOR SEGUE METHOD CALLED");

        UIViewController *toVC = segue.destinationViewController;
        [interactionController wireToViewController:toVC];
        toVC.transitioningDelegate = self;
        toVC.modalPresentationStyle = UIModalPresentationCustom;
      


#pragma mark UIViewControllerTransition Delegate Methods

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:  (UIViewController *)presented                                                                   presentingController:  (UIViewController *)presenting sourceController:(UIViewController *)source 
    NSLog(@"PRESENTING ANIMATION CONTROLLER CALLED");

    SwipeDownPresentationAnimationController *transitionController = [SwipeDownPresentationAnimationController new];
    return transitionController;


- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed 
    NSLog(@"DISMISS ANIMATION CONTROLLER CALLED");

    DismissAnimatorViewController *transitionController = [DismissAnimatorViewController new];
    return transitionController;


- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator 
    NSLog(@"Interaction controller for dimiss method caled");

    return interactionController.interactionInProgress ? interactionController:nil;

注意:交互滑动仅用于关闭 VC,这就是它在interactionControllerForDismissal 方法中的原因

这是解除动画的代码,当我点击按钮解除它时,它可以正常工作:

#import "DismissAnimatorViewController.h"

@implementation DismissAnimatorViewController

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext 
    return 1.0;


- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext 
    NSTimeInterval duration = [self transitionDuration:transitionContext];

    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    CGRect initialFrameFromVC = [transitionContext initialFrameForViewController:fromVC];

    UIView *containerView = [transitionContext containerView];

    CGRect screenBounds = [[UIScreen mainScreen] bounds];
    NSLog(@"The screen bounds is :%@", NSStringFromCGRect(screenBounds));
    toVC.view.frame = CGRectOffset(initialFrameFromVC, 0, screenBounds.size.height);
    toVC.view.alpha = 0.2;

    CGRect pushedPresentingFrame = CGRectOffset(initialFrameFromVC, 0, -screenBounds.size.height);

    [containerView addSubview:toVC.view];

    [UIView animateWithDuration:duration
                          delay:0
         usingSpringWithDamping:0.6
          initialSpringVelocity:0
                        options:UIViewAnimationOptionCurveEaseIn
                     animations:^
                         fromVC.view.frame = pushedPresentingFrame;
                         fromVC.view.alpha = 0.2;
                         toVC.view.frame = initialFrameFromVC;
                         toVC.view.alpha = 1.0;
                      completion:^(BOOL finished) 
                         [transitionContext completeTransition:YES];
                     ];


@end

这是用作交互控制器的 UIPercentDrivenInteractiveTransition 子类的代码:

#import "SwipeInteractionController.h"

@implementation SwipeInteractionController 
    BOOL _shouldCompleteTransition;
    UIViewController *_viewController;


- (void)wireToViewController:(UIViewController *)viewController 
    _viewController = viewController;
    [self prepareGestureRecognizerInView:_viewController.view];


- (void)prepareGestureRecognizerInView:(UIView*)view 
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    gesture.minimumNumberOfTouches = 1.0;

    [view addGestureRecognizer:gesture];


- (CGFloat)completionSpeed 
    return 1 - self.percentComplete;
    NSLog(@"PERCENT COMPLETE:%f",self.percentComplete);


- (void)handleGesture:(UIPanGestureRecognizer*)gestureRecognizer 
//    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];
    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview];

    switch (gestureRecognizer.state) 
        case UIGestureRecognizerStateBegan:
            // 1. Start an interactive transition!
            self.interactionInProgress = YES;
            [_viewController dismissViewControllerAnimated:YES completion:nil];
            break;
        case UIGestureRecognizerStateChanged: 
            // 2. compute the current position
            CGFloat fraction = fabsf(translation.y / 568);
            NSLog(@"Fraction is %f",fraction);
            fraction = fminf(fraction, 1.0);
            fraction = fmaxf(fraction, 0.0);
            // 3. should we complete?
            _shouldCompleteTransition = (fraction > 0.23);
            // 4. update the animation controller
            [self updateInteractiveTransition:fraction];
            NSLog(@"Percent complete:%f",self.percentComplete);
            break;
        
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled: 
            // 5. finish or cancel
            NSLog(@"UI GESTURE RECOGNIZER STATE CANCELED");
            self.interactionInProgress = NO;
            if (!_shouldCompleteTransition || gestureRecognizer.state == UIGestureRecognizerStateCancelled) 
                [self cancelInteractiveTransition];
                NSLog(@"Interactive Transition is cancled.");
                
            else 
                NSLog(@"Interactive Transition is FINISHED");
                [self finishInteractiveTransition];
            
            break;
        
        default:
            NSLog(@"Default is being called");
            break;
    


@end

再一次,当我现在运行代码并且我没有一直滑动以有目的地取消转换时,我只是得到了一个闪光,并出现了我想要滑动到的视图控制器。无论转换是完成还是取消,都会发生这种情况。

但是,当我通过按钮关闭时,我得到了动画视图控制器中指定的过渡。

【问题讨论】:

确保覆盖 UIPercentDrivenInteractiveTransition 开始、结束和更新动画的方法。我没有看到它们在您的代码中的任何地方实现。 好的,试试看,谢谢。 @AshFurrow:我确实阅读了您关于自定义转换的 Teehan+Lax 教程,其中您提到了覆盖 UIPercentDrivenInteractiveTransition 方法,但我不明白它的原因。有必要吗?我目前正处于没有它的实现中间,并且诚然我遇到了一些奇怪的行为(例如,完成动画从手势结束的百分比再次重复初始完成后,屏幕变黑交互式取消),但我不能 100% 确定这是原因。 Anyhoo,只是想知道您是否有理由进行覆盖。谢谢! 重写UIPercentDrivenInteractiveTransition 不是绝对必要的,但它是最简单且“Apple 友好”的方法。 遇到同样的问题。完成块永远不会被调用,转换完成也不会被取消。 IOS8。找到方法了吗,怎么解决? 【参考方案1】:

我可以在这里看到几个问题 - 虽然我不能确定这些是否会解决您的问题!

首先,您的动画控制器的 UIView 动画完成块具有以下内容:

[transitionContext completeTransition:YES];

而它应该根据交互控制器的结果返回完成,如下所示:

[transitionContext completeTransition:![transitionContext transitionWasCancelled]]

另外,我发现如果您告诉UIPercentDrivenInteractiveTransition 转换已 100% 完成,它不会调用动画控制器完成块。作为一种解决方法,我将其限制在 ~99.9%

https://github.com/ColinEberhardt/VCTransitionsLibrary/issues/4

我在这里创建了许多示例交互和动画控制器,您可能会发现它们很有用:

https://github.com/ColinEberhardt/VCTransitionsLibrary

【讨论】:

感谢科林的帮助。我试过你说的,但还是有问题。嗯..【参考方案2】:

我遇到了同样的问题。我尝试了上述修复和其他修复,但没有任何效果。然后我偶然发现了https://github.com/MrAlek/AWPercentDrivenInteractiveTransition,它修复了所有问题。

将其添加到项目后,只需将 UIPercentDrivenInteractiveTransition 替换为 AWPercentDrivenInteractiveTransition

此外,您必须在开始交互式过渡之前设置动画师。就我而言,我对UIViewControllerAnimatedTransitioningUIViewControllerInteractiveTransitioning 使用相同的类,所以我只是在init() 中使用了它:

init() 
    super.init()
    self.animator = self

【讨论】:

以上是关于如何完成交互式 UIViewController 过渡?的主要内容,如果未能解决你的问题,请参考以下文章

允许在下面呈现的 UIViewController 上的 UIViewController 上进行交互

如何使用完成按钮呈现包含 iOS App Store 的 UIViewController

UIViewController 交互式过渡 - 取消交互式关闭时呈现的视图消失

SwiftUI 和 UIKit 与旧 API 中的 UIViewController 要求交互

没有 UIViewController 的 UIView

UIViewController 停止在 UIWebView 上接收触摸事件