问题描述
我正试图模仿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图片
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 - 在关闭和滚动手势之间切换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!