【发布时间】:2017-08-18 06:29:11
【问题描述】:
Line messenger 应用程序(日本事实上的 Messenger 应用程序)中存在我试图模仿的行为。
基本上,它们有一个模态视图控制器,里面有一个滚动视图。当滚动动作到达其内容的顶部时,视图控制器会无缝切换到交互式解除动画。此外,当手势将视图返回到屏幕顶部时,控制权将返回到滚动视图。
这是它的外观的 gif。
在我的一生中,我无法弄清楚他们是如何做到的。我尝试了几种不同的方法,但都失败了,而且我没有想法。谁能指出我正确的方向?
EDIT2
为了澄清,我想模仿的行为不仅仅是简单地向下拖动窗口。我能做到,没问题。
我想知道相同的滚动手势(不抬起手指)如何触发解除转换,然后在视图被拖回原始位置后将控制权转移回滚动视图。
这是我想不通的部分。
结束 EDIT2
EDIT1
这是我目前所拥有的。我能够使用滚动视图委托方法来添加一个处理常规解除动画的目标选择器,但它仍然无法按预期工作。
我创建了一个 UIViewController 并带有 UIWebView 作为属性。然后我把它放在UINavigationController 中,以模态形式呈现。
导航控制器使用动画/过渡控制器进行常规交互式解除(可以通过在导航栏上打手势来完成)。
从这里开始,一切正常,但无法从滚动视图触发解雇。
导航控制器.h
@interface NavigationController : UINavigationController <UIViewControllerTransitioningDelegate>
@property (nonatomic, strong) UIPanGestureRecognizer *gestureRecog;
- (void)handleGesture:(UIPanGestureRecognizer*)gestureRecognizer;
@end
导航控制器.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
现在,我必须让手势处理在滚动视图到达顶部时触发。所以,这就是我在视图控制器中所做的。
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];
}
这仍然不能正常工作。即使在解除动画期间,滚动视图仍会随着手势滚动。
结束 EDIT1
【问题讨论】:
-
您需要在达到滚动偏移值时添加transitionViewController动画。查看视图控制器转换的示例。
-
嗨,我猜你现在成功了。您选择什么方法来获得正确的行为?谢谢:)
-
@AnthoPak 不幸的是,我从来没有让这个工作。不过,我确信这是对委托方法的一些巧妙使用。
-
@ABeard89 我发现了这个stackoverflow.com/a/50060144/4894980 我认为它可以适用于全屏模式视图控制器。可能是一个好的开始! :)
-
@ABeard89 你确定吗?我已经尝试过演示,它似乎能够在继续滚动滚动视图时处理取消解雇。
标签: ios uiscrollview uigesturerecognizer modalviewcontroller