ios - 像 Instagram iPhone 应用程序一样向右滑动时导航弹出 View 。我如何实现这一点?

标签 ios iphone objective-c uigesturerecognizer

我想在屏幕上向右滑动时弹出一个 View ,或者它像导航栏的后退按钮一样工作。

我正在使用:

self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

这行用于弹出导航 View 的代码对我来说很有效,但是当我从屏幕中间滑动时,它不会像 Instagram iPhone 应用程序那样工作。

这里我给出了 Instagram 应用程序的一个屏幕,您可以看到向右滑动弹出导航 View 的示例:

enter image description here

最佳答案

Apple 自动实现的“向右滑动弹出 VC”仅适用于屏幕左侧 ~2​​0 点。通过这种方式,他们可以确保不会干扰您应用的功能。假设您在屏幕上有一个 UIScrollView,并且您无法向右滑动,因为它会不断弹出 VC。这可不好。

Apple 说 here :

interactivePopGestureRecognizer

The gesture recognizer responsible for popping the top view controller off the navigation stack. (read-only)

@property(nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer

The navigation controller installs this gesture recognizer on its view and uses it to pop the topmost view controller off the navigation stack. You can use this property to retrieve the gesture recognizer and tie it to the behavior of other gesture recognizers in your user interface. When tying your gesture recognizers together, make sure they recognize their gestures simultaneously to ensure that your gesture recognizers are given a chance to handle the event.

因此您必须实现自己的UIGestureRecognizer,并将其行为绑定(bind)到您的UIViewControllerinteractivePopGestureRecognizer


编辑:

这是我构建的解决方案。您可以根据 UIViewControllerAnimatedTransitioning 委托(delegate)实现自己的转换。此解决方案有效,但尚未经过全面测试。

您将获得一个交互式 滑动过渡来弹出您的 ViewController。您可以从 View 中的任何位置向右滑动。

已知问题:如果您开始平移并在 View 宽度的一半之前停止,过渡将被取消(预期行为)。在此过程中, View 将重置为其原始框架。这是此动画中的视觉故障。

示例的类如下:

UINavigationController > ViewController > SecondViewController

CustomPopTr​​ansition.h:

#import <Foundation/Foundation.h>

@interface CustomPopTransition : NSObject <UIViewControllerAnimatedTransitioning>

@end

CustomPopTr​​ansition.m:

#import "CustomPopTransition.h"
#import "SecondViewController.h"
#import "ViewController.h"

@implementation CustomPopTransition

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.3;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {

    SecondViewController *fromViewController = (SecondViewController*)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    ViewController *toViewController = (ViewController*)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:toViewController.view];
    [containerView bringSubviewToFront:fromViewController.view];

    // Setup the initial view states
    toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];

    [UIView animateWithDuration:0.3 animations:^{

        fromViewController.view.frame = CGRectMake(toViewController.view.frame.size.width, fromViewController.view.frame.origin.y, fromViewController.view.frame.size.width, fromViewController.view.frame.size.height);

    } completion:^(BOOL finished) {

        // Declare that we've finished
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];

}

@end

SecondViewController.h:

#import <UIKit/UIKit.h>

@interface SecondViewController : UIViewController <UINavigationControllerDelegate>

@end

SecondViewController.m:

#import "SecondViewController.h"
#import "ViewController.h"
#import "CustomPopTransition.h"

@interface SecondViewController ()

@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactivePopTransition;

@end

@implementation SecondViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.navigationController.delegate = self;

    UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePopRecognizer:)];
    [self.view addGestureRecognizer:popRecognizer];
}

-(void)viewDidDisappear:(BOOL)animated {

    [super viewDidDisappear:animated];

    // Stop being the navigation controller's delegate
    if (self.navigationController.delegate == self) {
        self.navigationController.delegate = nil;
    }
}

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {

    // Check if we're transitioning from this view controller to a DSLSecondViewController
    if (fromVC == self && [toVC isKindOfClass:[ViewController class]]) {
        return [[CustomPopTransition alloc] init];
    }
    else {
        return nil;
    }
}

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {

    // Check if this is for our custom transition
    if ([animationController isKindOfClass:[CustomPopTransition class]]) {
        return self.interactivePopTransition;
    }
    else {
        return nil;
    }
}

- (void)handlePopRecognizer:(UIPanGestureRecognizer*)recognizer {

    // Calculate how far the user has dragged across the view
    CGFloat progress = [recognizer translationInView:self.view].x / (self.view.bounds.size.width * 1.0);
    progress = MIN(1.0, MAX(0.0, progress));

    if (recognizer.state == UIGestureRecognizerStateBegan) {
        NSLog(@"began");
        // Create a interactive transition and pop the view controller
        self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
        [self.navigationController popViewControllerAnimated:YES];
    }
    else if (recognizer.state == UIGestureRecognizerStateChanged) {
        NSLog(@"changed");
        // Update the interactive transition's progress
        [self.interactivePopTransition updateInteractiveTransition:progress];
    }
    else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {
        NSLog(@"ended/cancelled");
        // Finish or cancel the interactive transition
        if (progress > 0.5) {
            [self.interactivePopTransition finishInteractiveTransition];
        }
        else {
            [self.interactivePopTransition cancelInteractiveTransition];
        }

        self.interactivePopTransition = nil;
    }
}

@end

关于ios - 像 Instagram iPhone 应用程序一样向右滑动时导航弹出 View 。我如何实现这一点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22244688/

相关文章:

ios - 调用 [locationManager requestWhenInUseAuthorization] 时警报 View 自行消失;

ios - 名为 runner 的 SKSpriteNode 以相反的方式移动

iphone - 用于可缩放 UIScrollView 的矢量绘图

iOS Internet 可达性被调用两次

unit-testing - iOS:为 block 编写测试

ios - GKSession:暂停应用后不可见

ios - 如何将 ISO 639 语言代码转换为完整的英文名称?

ios - 如何制作有限制的 UITextField

iphone - iPhone 开发的卷页和翻页效果

objective-c - 如何创建一个 'wraps' 目标/选择器对的 block ?