我想在屏幕上向右滑动时弹出一个视图,或者它像导航栏的后退按钮一样工作。
我正在使用:
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
流行导航视图的这一行代码,对我来说很有用,但是当我在屏幕中间滑动时,这将无法像Instagram iPhone应用程序一样工作。
在这里,我给出了一个Instagram应用程序的屏幕,你可以看到滑动右侧弹出导航视图的示例:
答案 0 :(得分:14)
UIScrollView
,你无法向右滑动,因为它会不断弹出VC。这不太好。
Apple说here:
<强> interactivePopGestureRecognizer 强>
手势识别器负责将顶视图控制器弹出导航堆栈。 (只读)
@property(非原子,只读)UIGestureRecognizer * interactivePopGestureRecognizer
导航控制器在其视图上安装此手势识别器 并使用它从导航中弹出最顶层的视图控制器 堆。您可以使用此属性来检索手势识别器 并将其与用户中其他手势识别器的行为联系起来 接口。将手势识别器绑在一起时,请确保 他们同时认出他们的姿势,以确保你的 手势识别器有机会处理该事件。
因此,您必须实施自己的UIGestureRecognizer
,并将其行为与interactivePopGestureRecognizer
的{{1}}联系起来。
修改:
这是我建立的解决方案。您可以实现符合UIViewController
委托的自己的转换。此解决方案有效,但尚未经过全面测试。
您将获得交互式滑动过渡以弹出ViewControllers。您可以从视图中的任何位置向右滑动。
已知问题:如果启动平移并在视图宽度的一半之前停止,则转换将被取消(预期行为)。在此过程中,视图将重置为其原始帧。在这个动画中,它们是一个视觉故障。
示例的类如下:
UINavigationController&gt; ViewController&gt; SecondViewController
CustomPopTransition.h :
UIViewControllerAnimatedTransitioning
CustomPopTransition.m :
#import <Foundation/Foundation.h>
@interface CustomPopTransition : NSObject <UIViewControllerAnimatedTransitioning>
@end
SecondViewController.h :
#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.m :
#import <UIKit/UIKit.h>
@interface SecondViewController : UIViewController <UINavigationControllerDelegate>
@end
答案 1 :(得分:6)
这是Spynet的Swift版本的答案,只做了一些修改。首先,我为UIView
动画定义了一条线性曲线。其次,我在下方的视图中添加了半透明的黑色背景,以获得更好的效果。第三,我已经将UINavigationController
子类化了。这允许过渡应用于任何&#34; Pop&#34; UINavigationController中的转换。这是代码:
<强> 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 强>
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
}
}
}
答案 2 :(得分:4)
对UINavigationController
进行子类化,您可以添加UISwipeGestureRecognizer
来触发弹出操作:
.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
答案 3 :(得分:4)
确实没有必要为此推出自己的解决方案,对UINavigationController
进行子类化并引用内置手势就像解释here一样正常。
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
}()
}
如果这样做,还要实现以下UINavigationControllerDelegate
函数以避免在根视图控制器上出现奇怪的行为:
public func navigationController(_: UINavigationController,
didShow _: UIViewController, animated _: Bool) {
self.fullScreenPanGestureRecognizer.isEnabled = self.viewControllers.count > 1
}
答案 4 :(得分:1)
创建一个平移手势识别器,并在其中移动交互式弹出手势识别器的目标。
将您的识别器添加到推送的视图控制器的viewDidLoad中,瞧!
let popGestureRecognizer = self.navigationController!.interactivePopGestureRecognizer!
if let targets = popGestureRecognizer.value(forKey: "targets") as? NSMutableArray {
let gestureRecognizer = UIPanGestureRecognizer()
gestureRecognizer.setValue(targets, forKey: "targets")
self.view.addGestureRecognizer(gestureRecognizer)
}
答案 5 :(得分:0)
我通过使用此库https://github.com/ykyouhei/KYDrawerController实现了这一目标 我将屏幕宽度更改为_drawerWidth = self.view.bounds.size.width; 这是我想要的工作