iOS - 在关闭和滚动手势之间切换 [英] iOS - Switch between dismiss and scroll gestures

查看:119
本文介绍了iOS - 在关闭和滚动手势之间切换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正试图模仿Line Messenger应用程序(日本的事实上的信使应用程序)中的行为。



基本上,他们有一个模态视图内部滚动视图的控制器。当滚动动作到达其内容的顶部时,视图控制器无缝切换到交互式解雇动画。此外,当手势将视图返回到屏幕顶部时,控制权将返回到滚动视图。



这是一个gif的外观。





对于我的生活,我无法弄清楚他们是怎么回事做到了。我尝试了几种不同的方法,但它们都失败了,我没有想法。任何人都能指出我正确的方向吗?



EDIT2



为了澄清,我想要模仿的行为不仅仅是拖动窗口。我可以做到这一点,没问题。



我想知道同样的滚动手势(不抬起手指)会触发解雇转换,然后将控制转回滚动视图被拖回到原始位置后查看。



这是我无法弄清楚的部分。



结束EDIT2



EDIT1



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



我创建一个 UIViewController ,其中 UIWebView 作为属性。然后我把它放在一个 UINavigationController 中,它以模态方式显示。



导航控制器使用动画/转换控制器定期互动解雇(可以通过导航栏上的手势来完成)。



从这里,一切正常,但是无法从滚动触发解雇查看。



NavigationController.h

  @interface NavigationController:UINavigationController< ; UIViewControllerTransitioningDelegate> 

@property(非原子,强)UIPanGestureRecognizer * gestureRecog;

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

@end

NavigationController.m

  #importNavigationController.h
#importAnimationController.h
#importTransitionController.h

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

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

self.transitioningDelegate = self;

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

返回自我;
}

- (void)viewDidLoad {
[super viewDidLoad];

//设置手势识别器
self.gestureRecog = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture :)];
[self.view addGestureRecognizer:_gestureRecog];
}

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

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

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

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

switch(gestureRecog.state){
case UIGestureRecognizerStateBegan:{
_interactor.hasStarted = YES;
[self dismissViewControllerAnimated:YES completion:nil];
休息;
}
case UIGestureRecognizerStateChanged:{
if(!_interactor.hasStarted){
_interactor.hasStarted = YES;
[self dismissViewControllerAnimated:YES completion:nil];
}
_interactor.shouldFinish = downwardMovementPercent>阈;
[_intector updateInteractiveTransition:downwardMovementPercent];
休息;
}
案例UIGestureRecognizerStateCancelled:{
_interactor.hasStarted = NO;
[_intectorctor cancelInteractiveTransition];
休息;
}
案例UIGestureRecognizerStateEnded:{
_interactor.hasStarted = NO;
if(_interactor.shouldFinish){
[_interactor finishInteractiveTransition];
} else {
[_intectorctor cancelInteractiveTransition];
}
休息;
}
默认值:{
break;
}
}
}

@end

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



WebViewController.m

  #importWebViewController.h
#importNavigationController.h

@interface WebViewController()

@property(弱,非原子)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(@添加滚动目标);

_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)减速{
NSLog(@End End Dragging);
if(_isHandlingPan){
NSLog(@删除行动);
_isHandlingPan = NO;
[scrollView.panGestureRecognizer removeTarget:self action:@selector(handleGesture :)];
}
}
- (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer {
[(NavigationController *)self.navigationController handleGesture:gestureRecognizer];
}

这仍然无法正常工作。即使在解雇动画期间,滚动视图仍然会以手势滚动。



结束EDIT1

解决方案

这是一个自定义的交互式转换。



首先,你需要设置 transitioningDelegate of UIViewController

  id< UIViewControllerTransitioningDelegate> transitioningDelegate; 

然后将这两种方法实施到

  //向代表询问解雇视图控制器时要使用的转换动画对象。 
- animationControllerForDismissedController:
//要求你的代理人在解雇视图控制器时使用交互式动画制作者对象。
- interactionControllerForDismissal:

当拖到顶部,你开始转换,你可以使用 UIPercentDrivenInteractiveTransition 控制滚动过程中的进度。



您还可以参考 ZFDragableModalTransition



ZFDragableModalTransition图片




There's a behavior in the Line messenger app (the de facto messenger app in Japan) that I'm trying to emulate.

Basically, they have a modal view controller with a scroll view inside. When the scroll action reaches the top of its content, the view controller seamlessly switches to an interactive dismissal animation. Also, when the gesture returns the view to the top of the screen, control is returned to the scroll view.

Here's a gif of how it looks.

For the life of me, I can't figure out how they did it. I've tried a few different methods, but they've all failed, and I'm out of ideas. Can anyone point me in the right direction?

EDIT2

To clarify, the behavior that I want to emulate isn't just simply dragging the window down. I can do that, no problem.

I want to know how the same scroll gesture (without lifting the finger) triggers the dismissal transition and then transfers control back to the scroll view after the view has been dragged back to the original position.

This is the part that I can't figure out.

End EDIT2

EDIT1

Here's what I have so far. I was able to use the scroll view delegate methods to add a target-selector that handles the regular dismissal animation, but it still doesn't work as expected.

I create a UIViewController with a UIWebView as a property. Then I put it in a UINavigationController, which is presented modally.

The navigation controller uses animation/transition controllers for the regular interactive dismissal (which can be done by gesturing over the navigation bar).

From here, everything works fine, but the dismissal can't be triggered from the scroll view.

NavigationController.h

@interface NavigationController : UINavigationController <UIViewControllerTransitioningDelegate>

@property (nonatomic, strong) UIPanGestureRecognizer *gestureRecog;

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

@end

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

Now, I have to get that gesture handling to trigger when the scroll view has reached the top. So, here's what I did in the view controller.

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

This still doesn't work quite right. Even during the dismissal animation, the scroll view is still scrolling with the gesture.

End EDIT1

解决方案

That is a custom interactive transition.

First, you need set transitioningDelegate of UIViewController

id<UIViewControllerTransitioningDelegate> transitioningDelegate;

Then implment these two method to

 //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:

When drag to top, you start the transition, you may use UIPercentDrivenInteractiveTransition to control the progress during scrolling.

You can also refer to the source code of ZFDragableModalTransition

Image of ZFDragableModalTransition

这篇关于iOS - 在关闭和滚动手势之间切换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆