ChatGPT解决这个技术问题 Extra ChatGPT

设置 leftBarButtonItem 后如何在 UINavigationController 中启用后退/左滑手势?

我从 here 得到了相反的问题。默认情况下,在 iOS7 中,UINavigationController 堆栈的向后滑动手势可以弹出呈现的 ViewController。现在我为所有的 ViewControllers 统一了所有的 self.navigationItem.leftBarButtonItem 样式。

这是代码:

self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:LOADIMAGE(@"back_button") style:UIBarButtonItemStylePlain target:self action:@selector(popCurrentViewController)];

之后,navigationController.interactivePopGestureRecognizer 被禁用。如何在不删除自定义 leftBarButtonItem 的情况下启用弹出手势?

谢谢!

同样的问题已经有解决方案here
@ian 谢谢!这意味着所有的屏幕滑动手势都是用于向后滑动,我认为这不是一个好主意。

R
Rakesha Shastri

首先在 viewDidLoad 中设置委托:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

然后在推送时禁用手势:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [super pushViewController:viewController animated:animated];
    self.interactivePopGestureRecognizer.enabled = NO;
}

并在 viewDidDisappear 中启用:

self.navigationController.interactivePopGestureRecognizer.enabled = YES;

此外,将 UINavigationControllerDelegate 添加到您的视图控制器。


我忘了说你应该将 UINavigationControllerDelegate 添加到你的视图控制器中。
谢谢!我也刚刚在SO中找到了解决方案! (在问之前我没有搜索足够的结果,我的错!)
为什么要设置识别器的委托?您没有提到协议方法的任何实现吗?
这不起作用。 @hfossli 的解决方案非常有效。虽然我在 Swift3 中尝试过。
我们如何才能使这个通用并添加到现有项目中?我们当然不想对每个视图控制器都这样做......
u
user1447414

您需要处理两种情况:

当您将新视图推送到堆栈时当您显示根视图控制器时

如果你只需要一个可以使用的基类,这里有一个 Swift 3 版本:

import UIKit

final class SwipeNavigationController: UINavigationController {
    
    // MARK: - Lifecycle
    
    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)

         delegate = self
    }
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        
        delegate = self
    }

    required init?(coder aDecoder: NSCoder) { 
        super.init(coder: aDecoder) 

        delegate = self 
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // This needs to be in here, not in init
        interactivePopGestureRecognizer?.delegate = self
    }
    
    deinit {
        delegate = nil
        interactivePopGestureRecognizer?.delegate = nil
    }
    
    // MARK: - Overrides
    
    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        duringPushAnimation = true
        
        super.pushViewController(viewController, animated: animated)
    }
    
    // MARK: - Private Properties
    
    fileprivate var duringPushAnimation = false

}

// MARK: - UINavigationControllerDelegate

extension SwipeNavigationController: UINavigationControllerDelegate {
    
    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
        
        swipeNavigationController.duringPushAnimation = false
    }
    
}

// MARK: - UIGestureRecognizerDelegate

extension SwipeNavigationController: UIGestureRecognizerDelegate {
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard gestureRecognizer == interactivePopGestureRecognizer else {
            return true // default value
        }
        
        // Disable pop gesture in two situations:
        // 1) when the pop animation is in progress
        // 2) when user swipes quickly a couple of times and animations don't have time to be performed
        return viewControllers.count > 1 && duringPushAnimation == false
    }
}

如果您最终需要在另一个类中充当 UINavigationControllerDelegate,您可以编写委托转发器 similar to this answer

改编自 Objective-C 中的源代码:https://github.com/fastred/AHKNavigationController


这是一个伟大而干净的解决方案
它确实很好用,谢谢 - 但您也可以/应该实现 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) delegate = self } 以用于情节提要 :)
我添加了情节提要支持:stackoverflow.com/a/49750785/129202 似乎可以工作,但可以随时对其进行编辑以解决任何问题。
如果我不需要在某些屏幕上向后滑动怎么办。我需要在那些屏幕上做什么?我已经为这个委托方法返回了 false。 func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 并在 viewwillAppear 和消失方法中设置 true false self.navigationController?.interactivePopGestureRecognizer?.isEnabled 均无效。任何解决方案?
我怎么称呼这门课?
H
Husam

当我设置委托时它对我有用

self.navigationController.interactivePopGestureRecognizer.delegate = self;

然后实施

迅速

extension MyViewController:UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

Objective-C

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

您能否指出任何与“滑动返回”功能相关的苹果文档?
不幸的是,如果您在推送动画期间或在根视图控制器上滑动,这将冻结。我在这里发布了一个修复版本:stackoverflow.com/a/43433530/308315
这行得通!但为什么?谁能提供更多解释?为什么要求这个手势识别器通过另一个手势识别器失败以某种方式神奇地使它真正识别手势?
还在救人! 😍
Y
Yahya Tabba

它适用于我 Swift 3:

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

在 ViewDidLoad 中:

    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true

此外,如果你有一个 UIViewController,你也应该从 UIGestureRecognizerDelegate 继承。
这对我有用,但我不需要使用 .isEnabled 行。
不幸的是,如果您在推送动画期间或在根视图控制器上滑动,这将冻结。我在这里发布了一个修复版本:stackoverflow.com/a/43433530/308315
M
Mr. Bean

这是在 iOS 10、Swift 3 中启用/禁用滑动到弹出视图控制器的最佳方法:

对于第一个屏幕 [您要禁用滑动手势的位置]:

class SignUpViewController : UIViewController,UIGestureRecognizerDelegate {

//MARK: - View initializers
override func viewDidLoad() {
    super.viewDidLoad()
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    swipeToPop()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func swipeToPop() {

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true;
    self.navigationController?.interactivePopGestureRecognizer?.delegate = self;
}

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {

    if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
        return false
    }
    return true
} }

对于中间屏幕 [您要启用滑动手势的位置]:

class FriendListViewController : UIViewController {

//MARK: - View initializers
override func viewDidLoad() {

    super.viewDidLoad()
    swipeToPop()
}

func swipeToPop() {

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true;
    self.navigationController?.interactivePopGestureRecognizer?.delegate = nil;
} }

不幸的是,如果您在推送动画期间或在根视图控制器上滑动,这将冻结。我在这里发布了一个修复版本:stackoverflow.com/a/43433530/308315
@iwasrobbed 我在根视图控制器上检查了上面的代码,它运行良好我不知道为什么它最终失败了。感谢更新版本
这是完美的答案,@iwasrobbed 你的代码不起作用
@NitinBhatt,cn 如果答案对您有用,我请+1。谢谢
B
Burcu Kutluay

我不需要为它添加gestureRecognizer 函数。我在 viewDidLoad 中添加以下代码块就足够了:

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
}

这适用于我在 iOS 13.3 上! delegate = nil 部分是关键。
检查此答案以避免错误:stackoverflow.com/a/61668408/1887537
如果用户在第一个场景上向后滑动,这将产生一个错误。推荐使用这个stackoverflow.com/a/60091707/8264784
A
Adrian Mole

在 Swift 中,您可以执行以下代码

import UIKit
extension UINavigationController: UIGestureRecognizerDelegate {

    open override func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}

上面的代码有助于快速返回到之前的控制器,如 Facebook、Twitter。


J
Josh O'Connor

斯威夫特 3:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return (otherGestureRecognizer is UIScreenEdgePanGestureRecognizer)
}

不幸的是,如果您在推送动画期间或在根视图控制器上滑动,这将冻结。我在这里发布了一个修复版本:stackoverflow.com/a/43433530/308315
A
Andriy Gordiychuk

如果您希望在您的应用中随处可见这种行为并且不想向单个 viewDidAppear 等添加任何内容,那么您应该创建一个子类

class QFNavigationController:UINavigationController, UIGestureRecognizerDelegate, UINavigationControllerDelegate{
    override func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
        delegate = self
    }

    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        super.pushViewController(viewController, animated: animated)
        interactivePopGestureRecognizer?.isEnabled = false
    }

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        interactivePopGestureRecognizer?.isEnabled = true
    }

    // IMPORTANT: without this if you attempt swipe on
    // first view controller you may be unable to push the next one
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }

}

现在,无论您何时使用 QFNavigationController,您都可以获得所需的体验。


C
Community

设置自定义后退按钮会禁用向后滑动功能。

保留它的最佳方法是继承 UINavigationViewController 并将自己设置为 interactivePopGestureRecognizer 委托;然后您可以从 gestureRecognizerShouldBegin 返回 YES 以允许向后滑动。

例如,这是在 AHKNavigationController

还有一个 Swift 版本:https://stackoverflow.com/a/43433530/308315


隐藏导航栏时也会弹出禁用
J
Jonny

This 答案,但有情节提要支持。

class SwipeNavigationController: UINavigationController {

    // MARK: - Lifecycle

    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        self.setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.setup()
    }

    private func setup() {
        delegate = self
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // This needs to be in here, not in init
        interactivePopGestureRecognizer?.delegate = self
    }

    deinit {
        delegate = nil
        interactivePopGestureRecognizer?.delegate = nil
    }

    // MARK: - Overrides

    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        duringPushAnimation = true

        super.pushViewController(viewController, animated: animated)
    }

    // MARK: - Private Properties

    fileprivate var duringPushAnimation = false
}

b
budiDino

我创建了以下 Swift 5+ UIViewController 扩展程序,以便更轻松地在您需要的每个屏幕上添加/删除交互式弹出手势。

笔记:

在每个具有自定义后退按钮的屏幕上添加 enableInteractivePopGesture()

在 viewDidAppear 上为导航控制器的根屏幕添加 disableInteractivePopGesture() 以防止向后滑动问题这里提到的一些答案

还要在您不希望有后退按钮的推送屏幕上添加 disableInteractivePopGesture() 并向后滑动手势扩展 UIViewController:UIGestureRecognizerDelegate { func disableInteractivePopGesture() { navigationItem.hidesBackButton = true navigationController?.interactivePopGestureRecognizer?.delegate = self navigationController?。 interactivePopGestureRecognizer?.isEnabled = false } func enableInteractivePopGesture() { navigationController?.interactivePopGestureRecognizer?.delegate = self navigationController?.interactivePopGestureRecognizer?.isEnabled = true } }


s
slythfox

我们都在解决 some old bugs 可能因为它是“设计”而无法修复的问题。我在尝试取消 interactivePopGestureRecognizer 的委托时遇到了@iwasrobbed 在其他地方描述的冻结问题,这似乎应该有效。如果您希望滑动行为重新考虑使用您可以自定义的 backBarButtonItem

UINavigationBar 隐藏时,我还遇到了 interactivePopGestureRecognizer 不工作。如果隐藏导航栏对您来说是一个问题,请在实施错误的解决方法之前重新考虑您的设计。


m
mzaink

大多数答案都与代码相关。但我会给你一个在 Storyboard 上工作的。是的!你没看错。

单击主 UINavigationController 并导航到它的 Identity Inspector 选项卡。

在用户定义的运行时属性下,将名为 interactivePopGestureRecognizer.enabled 的单个运行时属性设置为 true。或者以图形方式,您必须启用复选框,如下图所示。

而已。你可以走了。你的后背姿势会像它一直在那里一样工作。

https://i.stack.imgur.com/HrDuA.jpg


E
Egzon P.

Swift 5,在 viewDidLoad 方法中只添加这两个:

override func viewDidLoad() {
    super.viewDidLoad()

    navigationController?.interactivePopGestureRecognizer?.delegate = self
    navigationController?.interactivePopGestureRecognizer?.isEnabled = true
}

m
mehmetdelikaya

我在启用和禁用与弹出视图控制器的滑动交互时遇到问题。

我有一个基本导航控制器,我的应用程序流程就像推送 Splash VC,推送 Main VC,然后推送 Some VC。

我想从 Some VC 返回到 Main VC。还要禁用滑动以防止从主 VC 返回到飞溅。

经过下面的一些尝试对我有用。

在 Main VC 中编写扩展以禁用滑动

extension MainViewController : UIGestureRecognizerDelegate{
    
    func disableSwipeToPop() {
        self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
        self.navigationController?.interactivePopGestureRecognizer?.delegate = self
    }
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        
        if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
            return false
        }
        return true
    }
}

在 Main VC 的 viewDidAppear 上调用 disableSwipeToPop() 方法

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.disableSwipeToPop()
}

在 Some VC 中编写扩展以启用滑动弹出 Some VC

extension SomeViewController{
    
    func enableSwipeToPop() {
        self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
        self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
    }
    
}

在某些 VC 的 viewDidLoad 上调用 enableSwipeToPop() 方法

override func viewDidLoad() {
        super.viewDidLoad()
        self.enableSwipeToPop()
}

而已。此外,如果您尝试在 viewWillAppear 上禁用滑动,当用户停止滑动以取消操作时,您可能会失去再次滑动的能力。


H
HDJEMAI

对于那些仍然遇到问题的人,请尝试将两行分开如下。

override func viewDidLoad() {
    self.navigationController!.interactivePopGestureRecognizer!.delegate = self
    ...

override func viewWillAppear(_ animated: Bool) {
    self.navigationController!.interactivePopGestureRecognizer!.isEnabled = true
    ...

显然,在我的应用程序中,

interactivePopGestureRecognizer!.isEnabled

由于某种原因,在视图显示之前重置为 false