[英]Navigation pop view when swipe right like Instagram iPhone app.How i achieve this?
I want to pop a view when swipe right on screen or it's work like back button of navigation bar. 我想在屏幕上向右滑动时弹出一个视图,或者它像导航栏的后退按钮一样工作。
I am using: 我在用:
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
This single line of code for pop navigation view and it's a work for me but when i swipe form middle of screen this will not work like Instagram iPhone app. 这个单行代码用于流行导航视图,它对我来说很有用,但是当我在屏幕中间滑动时,这将无法像Instagram iPhone应用程序那样工作。
Here i give a one screen of Instagram app in that you can see the Example of swipe right pop navigation view: 在这里,我给出了一个Instagram应用程序的屏幕,你可以看到滑动右侧弹出导航视图的示例:
Apple's automatic implementation of the "swipe right to pop VC" only works for the left ~20 points of the screen. Apple自动实现“刷卡直播VC”仅适用于屏幕左侧~20点。 This way, they make sure they don't mess with your app's functionalities.
这样,他们确保他们不会弄乱您的应用程序的功能。 Imagine you have a
UIScrollView
on screen, and you can't swipe right because it keeps poping VCs out. 想象一下,你在屏幕上有一个
UIScrollView
,你无法向右滑动,因为它不断弹出VC。 This wouldn't be nice. 这不会很好。
interactivePopGestureRecognizer
interactivePopGestureRecognizer
The gesture recognizer responsible for popping the top view controller off the navigation stack.
手势识别器负责将顶视图控制器弹出导航堆栈。 (read-only)
(只读)
@property(nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer
@property(非原子,只读)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.
将手势识别器绑在一起时,请确保他们同时识别手势,以确保您的手势识别器有机会处理事件。
So you will have to implement your own UIGestureRecognizer
, and tie its behavior to the interactivePopGestureRecognizer
of your UIViewController
. 因此,您必须实现自己的
UIGestureRecognizer
,并将其行为与UIViewController
的interactivePopGestureRecognizer
。
Edit : 编辑:
Here is a solution I built. 这是我建立的解决方案。 You can implement your own transition conforming to the
UIViewControllerAnimatedTransitioning
delegate. 您可以实现符合
UIViewControllerAnimatedTransitioning
委托的自己的转换。 This solution works , but has not been thoroughly tested. 此解决方案有效 ,但尚未经过全面测试。
You will get an interactive sliding transition to pop your ViewControllers. 您将获得一个交互式滑动过渡以弹出ViewControllers。 You can slide to right from anywhere in the view.
您可以从视图中的任何位置向右滑动。
Known issue : if you start the pan and stop before half the width of the view, the transition is canceled (expected behavior). 已知问题:如果启动平移并在视图宽度的一半之前停止,则会取消转换(预期行为)。 During this process, the views reset to their original frames.
在此过程中,视图将重置为其原始帧。 Their is a visual glitch during this animation.
在这个动画中,它们是一个视觉故障。
The classes of the example are the following : 示例的类如下:
UINavigationController > ViewController > SecondViewController
UINavigationController> ViewController> SecondViewController
CustomPopTransition.h : CustomPopTransition.h :
#import <Foundation/Foundation.h>
@interface CustomPopTransition : NSObject <UIViewControllerAnimatedTransitioning>
@end
CustomPopTransition.m : CustomPopTransition.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 : SecondViewController.h :
#import <UIKit/UIKit.h>
@interface SecondViewController : UIViewController <UINavigationControllerDelegate>
@end
SecondViewController.m : 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
Here's a Swift version of Spynet's answer, with a few modifications. 这是一个Swift版本的Spynet的答案,只有一些修改。 Firstly, I've defined a linear curve for the
UIView
animation. 首先,我为
UIView
动画定义了一条线性曲线。 Secondly, I've added a semi-transparent black background to the view underneath for a better effect. 其次,我在下面的视图中添加了一个半透明的黑色背景,以获得更好的效果。 Thirdly, I've subclassed a
UINavigationController
. 第三,我已经将
UINavigationController
子类化了。 This allows the transition to be applied to any "Pop" transition within the UINavigationController. 这允许将转换应用于UINavigationController中的任何“Pop”转换。 Here's the code:
这是代码:
CustomPopTransition.swift CustomPopTransition.swift
import UIKit
class CustomPopTransition: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
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: UIView.AnimationOptions.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 PoppingNavigationController.swift
import UIKit
class PoppingNavigationController : UINavigationController, UINavigationControllerDelegate {
var interactivePopTransition: UIPercentDrivenInteractiveTransition!
override func viewDidLoad() {
self.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
addPanGesture(viewController: viewController)
}
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if (operation == .pop) {
return CustomPopTransition()
}
else {
return nil
}
}
func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
if animationController.isKind(of: CustomPopTransition.self) {
return interactivePopTransition
}
else {
return nil
}
}
func addPanGesture(viewController: UIViewController) {
let popRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanRecognizer(recognizer:)))
viewController.view.addGestureRecognizer(popRecognizer)
}
@objc
func handlePanRecognizer(recognizer: UIPanGestureRecognizer) {
// Calculate how far the user has dragged across the view
var progress = recognizer.translation(in: 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.popViewController(animated: true)
}
else if (recognizer.state == .changed) {
// Update the interactive transition's progress
interactivePopTransition.update(progress)
}
else if (recognizer.state == .ended || recognizer.state == .cancelled) {
// Finish or cancel the interactive transition
if (progress > 0.5) {
interactivePopTransition.finish()
}
else {
interactivePopTransition.cancel()
}
interactivePopTransition = nil
}
}
}
There really is no need to roll your own solution for this, sub-classing UINavigationController
and referencing the built-in gesture works just fine as explained here . 真的是没有必要推出这个自己的解决方案,子类
UINavigationController
和引用内置手势可以作为解释就好了这里 。
The same solution in Swift: Swift中的相同解决方案:
public final class MyNavigationController: UINavigationController {
public override func viewDidLoad() {
super.viewDidLoad()
self.view.addGestureRecognizer(self.fullScreenPanGestureRecognizer)
}
private lazy var fullScreenPanGestureRecognizer: UIPanGestureRecognizer = {
let gestureRecognizer = UIPanGestureRecognizer()
if let cachedInteractionController = self.value(forKey: "_cachedInteractionController") as? NSObject {
let string = "handleNavigationTransition:"
let selector = Selector(string)
if cachedInteractionController.responds(to: selector) {
gestureRecognizer.addTarget(cachedInteractionController, action: selector)
}
}
return gestureRecognizer
}()
}
If you do this, also implement the following UINavigationControllerDelegate
function to avoid strange behaviour at the root view controller: 如果这样做,还要实现以下
UINavigationControllerDelegate
函数以避免在根视图控制器上出现奇怪的行为:
public func navigationController(_: UINavigationController,
didShow _: UIViewController, animated _: Bool) {
self.fullScreenPanGestureRecognizer.isEnabled = self.viewControllers.count > 1
}
Subclassing the UINavigationController
you can add a UISwipeGestureRecognizer
to trigger the pop action: 对
UINavigationController
子类化,您可以添加UISwipeGestureRecognizer
来触发弹出操作:
.h file: .h文件:
#import <UIKit/UIKit.h>
@interface CNavigationController : UINavigationController
@end
.m file: .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
i achieved this by using this library https://github.com/ykyouhei/KYDrawerController i changed width of screen to _drawerWidth = self.view.bounds.size.width; 我通过使用这个库实现了这个https://github.com/ykyouhei/KYDrawerController我将屏幕宽度更改为_drawerWidth = self.view.bounds.size.width; which worked as i wanted
这是我想要的
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.