导航popup视图时,像刷Instagram的iPhone应用程序。如何实现这一目标?

我想在屏幕上向右滑动时popup视图,或者像导航栏的后退button那样工作。

我在用:

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

这一行的stream行导航视图的代码,这是一个工作,但当我滑过屏幕中间,这不会像Instagram的iPhone应用程序工作。

在这里,我给你一个Instagram的应用程序的屏幕,你可以看到右刷新权限popup导航视图的示例:

在这里输入图像说明

苹果自动执行的“向右popupVC”仅适用于屏幕的左〜20点。 这样,他们确保他们不会弄乱你的应用程序的function。 想象一下,你在屏幕上有一个UIScrollView ,你不能向右滑动,因为它不断popupVCs。 这不会很好。

苹果在这里说:

interactivePopGestureRecognizer

负责将顶视图控制器从导航堆栈popup的手势识别器。 (只读)

@property(非primefaces,只读)UIGestureRecognizer * interactivePopGestureRecognizer

导航控制器在其视图上安装此手势识别器,并使用该手势识别器将最上面的视图控制器popup导航堆栈。 您可以使用此属性来检索手势识别器,并将其绑定到用户界面中其他手势识别器的行为。 将手势识别器绑定在一起时,确保他们同时识别手势,以确保您的手势识别器有机会处理事件。

所以你将不得不实现自己的UIGestureRecognizer ,并将其行为绑定到UIViewControllerinteractivePopGestureRecognizer


编辑:

这是我build立的解决scheme。 你可以实现你自己的转换符合UIViewControllerAnimatedTransitioning委托。 这个解决scheme有效 ,但还没有经过彻底的testing。

您将得到一个交互式滑动转换来popup您的ViewControllers。 您可以从视图的任何位置向右滑动。

已知问题:如果启动平移并在视图宽度的一半之前停止,则会取消转换(预期行为)。 在此过程中,视图重置为其原始帧。 在这个animation中,他们是一个视觉问题。

示例的类如下所示:

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 

UINavigationController子类化,您可以添加一个UISwipeGestureRecognizer来触发pop操作:

.h文件:

 #import <UIKit/UIKit.h> @interface CNavigationController : UINavigationController @end 

.m文件:

 #import "CNavigationController.h" @interface CNavigationController ()<UIGestureRecognizerDelegate, UINavigationControllerDelegate> @property (nonatomic, retain) UISwipeGestureRecognizer *swipeGesture; @end @implementation CNavigationController #pragma mark - View cycles - (void)viewDidLoad { [super viewDidLoad]; __weak CNavigationController *weakSelf = self; self.delegate = weakSelf; self.swipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(gestureFired:)]; [self.view addGestureRecognizer:self.swipeGesture]; } #pragma mark - gesture method -(void)gestureFired:(UISwipeGestureRecognizer *)gesture { if (gesture.direction == UISwipeGestureRecognizerDirectionRight) { [self popViewControllerAnimated:YES]; } } #pragma mark - UINavigation Controller delegate - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { self.swipeGesture.enabled = NO; [super pushViewController:viewController animated:animated]; } #pragma mark UINavigationControllerDelegate - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animate { self.swipeGesture.enabled = YES; } @end 

这里是Spynet的答案的一个Swift版本,有一些修改。 首先,我为UIViewanimation定义了一条线性曲线。 其次,我在下面的视图中添加了一个半透明的黑色背景,以获得更好的效果。 第三,我已经分类了一个UINavigationController 。 这允许转换应用于UINavigationController中的任何“Pop”转换。 代码如下:

CustomPopTr​​ansition.swift

 import UIKit class CustomPopTransition: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { return 0.3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return } let containerView = transitionContext.containerView containerView.insertSubview(toViewController.view, belowSubview: fromViewController.view) // Setup the initial view states toViewController.view.frame = CGRect(x: -100, y: toViewController.view.frame.origin.y, width: fromViewController.view.frame.size.width, height: fromViewController.view.frame.size.height) let dimmingView = UIView(frame: CGRect(x: 0,y: 0, width: toViewController.view.frame.width, height: toViewController.view.frame.height)) dimmingView.backgroundColor = UIColor.black dimmingView.alpha = 0.5 toViewController.view.addSubview(dimmingView) UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: UIViewAnimationOptions.curveLinear, animations: { dimmingView.alpha = 0 toViewController.view.frame = transitionContext.finalFrame(for: toViewController) fromViewController.view.frame = CGRect(x: toViewController.view.frame.size.width, y: fromViewController.view.frame.origin.y, width: fromViewController.view.frame.size.width, height: fromViewController.view.frame.size.height) }, completion: { finished in dimmingView.removeFromSuperview() transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } ) } } 

PoppingNavigationController.swift

 import UIKit class PoppingNavigationController : UINavigationController, UINavigationControllerDelegate { var interactivePopTransition: UIPercentDrivenInteractiveTransition! override func viewDidLoad() { self.delegate = self } func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) { addPanGesture(viewController) } func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { if (operation == .Pop) { return CustomPopTransition() } else { return nil } } func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { if animationController.isKindOfClass(CustomPopTransition) { return interactivePopTransition } else { return nil } } func addPanGesture(viewController: UIViewController) { let popRecognizer = UIPanGestureRecognizer(target: self, action: Selector("handlePanRecognizer:")) viewController.view.addGestureRecognizer(popRecognizer) } func handlePanRecognizer(recognizer: UIPanGestureRecognizer) { // Calculate how far the user has dragged across the view var progress = recognizer.translationInView(self.view).x / self.view.bounds.size.width progress = min(1, max(0, progress)) if (recognizer.state == .Began) { // Create a interactive transition and pop the view controller self.interactivePopTransition = UIPercentDrivenInteractiveTransition() self.popViewControllerAnimated(true) } else if (recognizer.state == .Changed) { // Update the interactive transition's progress interactivePopTransition.updateInteractiveTransition(progress) } else if (recognizer.state == .Ended || recognizer.state == .Cancelled) { // Finish or cancel the interactive transition if (progress > 0.5) { interactivePopTransition.finishInteractiveTransition() } else { interactivePopTransition.cancelInteractiveTransition() } interactivePopTransition = nil } } } 

我通过使用这个库实现了这个https://github.com/ykyouhei/KYDrawerController我改变了屏幕的宽度_drawerWidth = self.view.bounds.size.width; 这是我想要的