iOS-在解除手势和滚动手势之间切换

ABe*_*d89 6 uiscrollview uigesturerecognizer modalviewcontroller ios

我正在尝试模仿Line Messenger应用程序(在日本实际上是Messenger应用程序)中的一种行为。

基本上,它们具有内部带有滚动视图的模态视图控制器。当滚动动作到达其内容的顶部时,视图控制器将无缝切换到交互式解雇动画。同样,当手势将视图返回到屏幕顶部时,控件将返回到滚动视图。

这是它外观的Gif。

演示gif

对于我的一生,我不知道他们是如何做到的。我尝试了几种不同的方法,但是它们都失败了,而且我没有主意。谁能指出我正确的方向?

编辑2

要澄清的是,我要模仿的行为不仅仅是将窗口向下拖动。我可以做到,没问题。

我想知道相同的滚动手势(不松开手指)如何触发撤消过渡,然后在视图被拖动回到原始位置后将控制权转移回滚动视图。

这是我不知道的部分。

结束EDIT2

编辑1

到目前为止,这就是我所拥有的。我能够使用滚动视图委托方法来添加可处理常规解雇动画的目标选择器,但仍无法按预期工作。

我创建了一个UIViewControllerUIWebView作为的属性。然后,我将其放入,以UINavigationController模态形式显示。

导航控制器将动画/过渡控制器用于常规的交互式解雇(可以通过在导航栏上进行手势来完成)。

从这里开始,一切正常,但是无法从滚动视图中触发解雇。

NavigationController.h

@interface NavigationController : UINavigationController <UIViewControllerTransitioningDelegate>

@property (nonatomic, strong) UIPanGestureRecognizer *gestureRecog;

- (void)handleGesture:(UIPanGestureRecognizer*)gestureRecognizer;

@end
Run Code Online (Sandbox Code Playgroud)

NavigationController.m

#import "NavigationController.h"
#import "AnimationController.h"
#import "TransitionController.h"

@implementation NavigationController {
    AnimationController *_animator;
    TransitionController *_interactor;
}

- (instancetype)init {
    self = [super init];

    self.transitioningDelegate = self;

    _animator = [[AnimationController alloc] init];
    _interactor = [[TransitionController alloc] init];

    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // Set the gesture recognizer
    self.gestureRecog = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    [self.view addGestureRecognizer:_gestureRecog];
}

- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {
    if (animator == _animator && _interactor.hasStarted) {
        return _interactor;
    }
    return nil;
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    if (dismissed == self || [self.viewControllers indexOfObject:dismissed] != NSNotFound) {
        return _animator;
    }
    return nil;
}

- (void)handleGesture:(UIPanGestureRecognizer *)gestureRecog {
    CGFloat threshold = 0.3f;

    CGPoint translation = [gestureRecog translationInView:self.view];
    CGFloat verticalMovement = translation.y / self.view.bounds.size.height;
    CGFloat downwardMovement = fmaxf(verticalMovement, 0.0f);
    CGFloat downwardMovementPercent = fminf(downwardMovement, 1.0f);

    switch (gestureRecog.state) {
        case UIGestureRecognizerStateBegan: {
            _interactor.hasStarted = YES;
            [self dismissViewControllerAnimated:YES completion:nil];
            break;
        }
        case UIGestureRecognizerStateChanged: {
            if (!_interactor.hasStarted) {
                _interactor.hasStarted = YES;
                [self dismissViewControllerAnimated:YES completion:nil];
            }
            _interactor.shouldFinish = downwardMovementPercent > threshold;
            [_interactor updateInteractiveTransition:downwardMovementPercent];
            break;
        }
        case UIGestureRecognizerStateCancelled: {
            _interactor.hasStarted = NO;
            [_interactor cancelInteractiveTransition];
            break;
        }
        case UIGestureRecognizerStateEnded: {
            _interactor.hasStarted = NO;
            if (_interactor.shouldFinish) {
                [_interactor finishInteractiveTransition];
            } else {
                [_interactor cancelInteractiveTransition];
            }
            break;
        }
        default: {
            break;
        }
    }
}

@end
Run Code Online (Sandbox Code Playgroud)

现在,我必须获得手势处理才能在滚动视图到达顶部时触发。所以,这就是我在视图控制器中所做的。

WebViewController.m

#import "WebViewController.h"
#import "NavigationController.h"

@interface WebViewController ()

@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end

@implementation WebViewController {
    BOOL _isHandlingPan;
    CGPoint _topContentOffset;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.webView.scrollView setDelegate:self];
}    

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if ((scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan ||
         scrollView.panGestureRecognizer.state == UIGestureRecognizerStateChanged) &&
        ! _isHandlingPan &&
        scrollView.contentOffset.y < self.navigationController.navigationBar.translucent ? -64.0f : 0) {

        NSLog(@"Adding scroll target");

        _topContentOffset = CGPointMake(scrollView.contentOffset.x, self.navigationController.navigationBar.translucent ? -64.0f : 0);
        _isHandlingPan = YES;
        [scrollView.panGestureRecognizer addTarget:self action:@selector(handleGesture:)];
    }
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    NSLog(@"Did End Dragging");
    if (_isHandlingPan) {
        NSLog(@"Removing action");
        _isHandlingPan = NO;
        [scrollView.panGestureRecognizer removeTarget:self action:@selector(handleGesture:)];
    }
}
- (void)handleGesture:(UIPanGestureRecognizer*)gestureRecognizer {
    [(NavigationController*)self.navigationController handleGesture:gestureRecognizer];
}
Run Code Online (Sandbox Code Playgroud)

这仍然不太正确。即使在解雇动画期间,滚动视图仍会随着手势滚动。

结束EDIT1

Leo*_*Leo 2

这是一个自定义的交互式过渡。

首先,你需要transitioningDelegate一套UIViewController

id<UIViewControllerTransitioningDelegate> transitioningDelegate;
Run Code Online (Sandbox Code Playgroud)

然后实现这两个方法

 //Asks your delegate for the transition animator object to use when dismissing a view controller.
 - animationControllerForDismissedController:
 //Asks your delegate for the interactive animator object to use when dismissing a view controller.
 - interactionControllerForDismissal:
Run Code Online (Sandbox Code Playgroud)

当拖动到顶部时,您开始过渡,您可以用来UIPercentDrivenInteractiveTransition控制滚动过程中的进度。

也可以参考ZFDragableModalTransition的源码

ZFDragableModalTransition 的图像