首页 文章

如何完成交互式UIViewController转换?

提问于
浏览
13

我一直在尝试使用新的iOS 7自定义转换API,并查看了我能找到的所有教程/文档,但我似乎无法根据我的具体情况计算出这些内容 .

基本上我正在尝试实现的是一个视图上的UIPanGestureRecognizer,我将向上滑动并转换到VC,当我将手指向上移动时,当视图从底部向上滑动时VC会向上滑动 .

没有交互过渡,我没有遇到任何问题,但是一旦我实现了交互(平移手势),我似乎无法完成转换 .

Here's the relevant code from the VC that conforms to the UIViewControllerTransitionDelegate which is needed to vend the animator controllers:

- (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;
}

NOTE: The interaction swipe is only for the dismissal of the VC which is why it's in the interactionControllerForDismissal method

Here's the code for the animator of the dismissal which works fine when I tap on a button to dismiss it:

#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

Here's the code for the UIPercentDrivenInteractiveTransition subclass which serves as the interaction controller:

#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

Once again, when I run the code now and I don't swipe all the way to purposefully cancel the transition, I just get a flash and am presented with the view controller I want to swipe to. This happens regardless if the transition completes or is canceled.

However, when I dismiss via the button I get the transition specified in my animator view controller.

2 回答

  • 22

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

    首先,动画控制器的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
    }
    

相关问题