J'adore le pack swipe hérité de l'intégration de vos vues dans un fichier UINavigationController
. Malheureusement, je n'arrive pas à trouver un moyen de masquer le NavigationBar
mais j'ai toujours le balayage tactile en arrière gesture
. Je peux écrire des gestes personnalisés mais je préfère ne pas le faire et me fier plutôt au UINavigationController
balayage arrière gesture
.
si je le décoche dans le storyboard, le balayage arrière ne fonctionne pas
sinon, si je le cache par programme, le même scénario.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
N'y a-t-il aucun moyen de masquer le haut NavigationBar
et de toujours avoir le balayage?
Réponses:
Un hack qui fonctionne est de régler le
interactivePopGestureRecognizer
délégué » duUINavigationController
à lanil
manière suivante:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
Mais dans certaines situations, cela pourrait créer des effets étranges.
la source
gestureRecognizerShouldBegin
, en retournanttrue
si le nombrenavigationController
de sviewController
est supérieur à 0.viewWillDisappear
et jusqu'à présent, je n'ai pas eu d'effets secondaires indésirables.Problèmes avec d'autres méthodes
Le réglage
interactivePopGestureRecognizer.delegate = nil
a des effets secondaires inattendus.Le paramètre
navigationController?.navigationBar.hidden = true
fonctionne, mais ne permet pas de masquer votre modification dans la barre de navigation.Enfin, il est généralement préférable de créer un objet modèle correspondant
UIGestureRecognizerDelegate
à votre contrôleur de navigation. Le paramétrer sur un contrôleur de laUINavigationController
pile est à l'origine desEXC_BAD_ACCESS
erreurs.Solution complète
Tout d'abord, ajoutez cette classe à votre projet:
class InteractivePopRecognizer: NSObject, UIGestureRecognizerDelegate { var navigationController: UINavigationController init(controller: UINavigationController) { self.navigationController = controller } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return navigationController.viewControllers.count > 1 } // This is necessary because without it, subviews of your top controller can // cancel out your gesture recognizer on the edge. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } }
Ensuite, définissez votre contrôleur de navigation
interactivePopGestureRecognizer.delegate
sur une instance de votre nouvelleInteractivePopRecognizer
classe.var popRecognizer: InteractivePopRecognizer? override func viewDidLoad() { super.viewDidLoad() setInteractiveRecognizer() } private func setInteractiveRecognizer() { guard let controller = navigationController else { return } popRecognizer = InteractivePopRecognizer(controller: controller) controller.interactivePopGestureRecognizer?.delegate = popRecognizer }
Profitez d'une barre de navigation cachée sans effets secondaires, qui fonctionne même si votre contrôleur supérieur possède des sous-vues de table, de collection ou de défilement.
la source
Dans mon cas, pour éviter les effets étranges
Contrôleur de vue racine
override func viewDidLoad() { super.viewDidLoad() // Enable swipe back when no navigation bar navigationController?.interactivePopGestureRecognizer?.delegate = self } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if(navigationController!.viewControllers.count > 1){ return true } return false }
http://www.gampood.com/pop-viewcontroller-with-out-navigation-bar/
la source
EXEC_BAD_ACCESS
UIGestureRecognizerDelegate
au contrôleur de vue racine ... Dans mon cas, le délégué a été défini sur nil dans un contrôleur de vue plus récent que le contrôleur de vue racine, donc lorsqu'il est retourné au contrôleur de vue racine, ilgestureRecognizerShouldBegin
n'a pas été appelé. Alors j'ai placé le.delegate = self
dedansviewDidAppear()
. Cela a résolu les effets étranges dans mon cas .. Cheers!EXEC_BAD_ACCESS
cela se produit?EXC_BAD_ACCESS
erreur: stackoverflow.com/questions/28746123/…Mis à jour pour iOS 13.4
iOS 13.4 a cassé la solution précédente, donc les choses vont devenir moche. Il semble que dans iOS 13.4, ce comportement est désormais contrôlé par une méthode privée
_gestureRecognizer:shouldReceiveEvent:
(à ne pas confondre avec la nouvelleshouldReceive
méthode publique ajoutée dans iOS 13.4).J'ai trouvé que d'autres solutions publiées remplaçant le délégué ou le définissant sur nul provoquaient un comportement inattendu.
Dans mon cas, lorsque j'étais au sommet de la pile de navigation et que j'essayais d'utiliser le geste pour en faire apparaître un de plus, cela échouait (comme prévu), mais les tentatives ultérieures de pousser sur la pile commenceraient à provoquer d'étranges problèmes graphiques dans le barre de navigation. Cela a du sens, car le délégué est utilisé pour gérer plus que simplement s'il faut ou non empêcher le geste d'être reconnu lorsque la barre de navigation est masquée et que tous ces autres comportements ont été rejetés.
D'après mes tests, il semble que ce
gestureRecognizer(_:, shouldReceiveTouch:)
soit la méthode que le délégué d'origine met en œuvre pour empêcher le geste d'être reconnu lorsque la barre de navigation est masquée, nongestureRecognizerShouldBegin(_:)
. D'autres solutions qui implémententgestureRecognizerShouldBegin(_:)
dans leur travail de délégué parce que l'absence d'une implémentation degestureRecognizer(_:, shouldReceiveTouch:)
provoquera le comportement par défaut de recevoir toutes les touches.La solution de @Nathan Perry se rapproche, mais sans implémentation de
respondsToSelector(_:)
, le code UIKit qui envoie des messages au délégué croira qu'il n'y a pas d'implémentation pour aucune des autres méthodes de délégué, etforwardingTargetForSelector(_:)
ne sera jamais appelé.Donc, nous prenons le contrôle de `gestureRecognizer (_ :, shouldReceiveTouch :) dans le scénario spécifique, nous voulons modifier le comportement, et sinon transmettre tout le reste au délégué.
class AlwaysPoppableNavigationController : UINavigationController { private var alwaysPoppableDelegate: AlwaysPoppableDelegate! override func viewDidLoad() { super.viewDidLoad() self.alwaysPoppableDelegate = AlwaysPoppableDelegate(navigationController: self, originalDelegate: self.interactivePopGestureRecognizer!.delegate!) self.interactivePopGestureRecognizer!.delegate = self.alwaysPoppableDelegate } } private class AlwaysPoppableDelegate : NSObject, UIGestureRecognizerDelegate { weak var navigationController: AlwaysPoppableNavigationController? weak var originalDelegate: UIGestureRecognizerDelegate? init(navigationController: AlwaysPoppableNavigationController, originalDelegate: UIGestureRecognizerDelegate) { self.navigationController = navigationController self.originalDelegate = originalDelegate } // For handling iOS before 13.4 @objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if let navigationController = navigationController, navigationController.isNavigationBarHidden && navigationController.viewControllers.count > 1 { return true } else if let originalDelegate = originalDelegate { return originalDelegate.gestureRecognizer!(gestureRecognizer, shouldReceive: touch) } else { return false } } // For handling iOS 13.4+ @objc func _gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceiveEvent event: UIEvent) -> Bool { if let navigationController = navigationController, navigationController.isNavigationBarHidden && navigationController.viewControllers.count > 1 { return true } else if let originalDelegate = originalDelegate { let selector = #selector(_gestureRecognizer(_:shouldReceiveEvent:)) if originalDelegate.responds(to: selector) { let result = originalDelegate.perform(selector, with: gestureRecognizer, with: event) return result != nil } } return false } override func responds(to aSelector: Selector) -> Bool { if #available(iOS 13.4, *) { // iOS 13.4+ does not need to override responds(to:) behavior, it only uses forwardingTarget return originalDelegate?.responds(to: aSelector) ?? false } else { if aSelector == #selector(gestureRecognizer(_:shouldReceive:)) { return true } else { return originalDelegate?.responds(to: aSelector) ?? false } } } override func forwardingTarget(for aSelector: Selector) -> Any? { if #available(iOS 13.4, *), aSelector == #selector(_gestureRecognizer(_:shouldReceiveEvent:)) { return nil } else { return self.originalDelegate } } }
la source
gestureRecognizerShouldBegin:
chose, et cela "semble fonctionner". Je me demande si je devrais faire attention.navigationController
c'était une référence forte dans AlwaysPoppableDelegate. J'ai modifié le code pour en faire uneweak
référence.Vous pouvez sous-classer UINavigationController comme suit:
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @end
La mise en oeuvre:
@implementation CustomNavigationController - (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated { [super setNavigationBarHidden:hidden animated:animated]; self.interactivePopGestureRecognizer.delegate = self; } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (self.viewControllers.count > 1) { return YES; } return NO; } @end
la source
UIPageViewController
survol.Réponse simple, sans effet secondaire
Bien que la plupart des réponses ici soient bonnes, elles ont apparemment des effets secondaires involontaires (rupture d'application) ou sont verbeuses.
La solution la plus simple mais la plus fonctionnelle que j'ai pu trouver était la suivante:
Dans le ViewController que vous masquez la navigationBar,
class MyNoNavBarViewController: UIViewController { // needed for reference when leaving this view controller var initialInteractivePopGestureRecognizerDelegate: UIGestureRecognizerDelegate? override func viewDidLoad() { super.viewDidLoad() // we will need a reference to the initial delegate so that when we push or pop.. // ..this view controller we can appropriately assign back the original delegate initialInteractivePopGestureRecognizerDelegate = self.navigationController?.interactivePopGestureRecognizer?.delegate } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) // we must set the delegate to nil whether we are popping or pushing to.. // ..this view controller, thus we set it in viewWillAppear() self.navigationController?.interactivePopGestureRecognizer?.delegate = nil } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(true) // and every time we leave this view controller we must set the delegate back.. // ..to what it was originally self.navigationController?.interactivePopGestureRecognizer?.delegate = initialInteractivePopGestureRecognizerDelegate } }
D'autres réponses suggèrent simplement de fixer le délégué à zéro. Glisser vers l'arrière vers le contrôleur de vue initial sur la pile de navigation entraîne la désactivation de tous les gestes. Une sorte de surveillance, peut-être, des développeurs UIKit / UIGesture.
De plus, certaines réponses que j'ai implémentées ici ont abouti à un comportement de navigation Apple non standard (en particulier, permettant la possibilité de faire défiler vers le haut ou vers le bas tout en glissant vers l'arrière). Ces réponses semblent également un peu verbeuses et dans certains cas incomplètes.
la source
viewDidLoad()
n'est pas un bon endroit pour capturerinitialInteractivePopGestureRecognizerDelegate
car celanavigationController
pourrait être nul (pas encore poussé sur la pile).viewWillAppear
de l'endroit où vousEn m'appuyant sur la réponse de Hunter Maximillion Monk , j'ai créé une sous-classe pour UINavigationController, puis défini la classe personnalisée pour mon UINavigationController dans mon storyboard. Le code final des deux classes ressemble à ceci:
InteractivePopRecognizer:
class InteractivePopRecognizer: NSObject { // MARK: - Properties fileprivate weak var navigationController: UINavigationController? // MARK: - Init init(controller: UINavigationController) { self.navigationController = controller super.init() self.navigationController?.interactivePopGestureRecognizer?.delegate = self } } extension InteractivePopRecognizer: UIGestureRecognizerDelegate { func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return (navigationController?.viewControllers.count ?? 0) > 1 } // This is necessary because without it, subviews of your top controller can cancel out your gesture recognizer on the edge. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } }
HiddenNavBarNavigationController:
class HiddenNavBarNavigationController: UINavigationController { // MARK: - Properties private var popRecognizer: InteractivePopRecognizer? // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() setupPopRecognizer() } // MARK: - Setup private func setupPopRecognizer() { popRecognizer = InteractivePopRecognizer(controller: self) } }
Storyboard:
la source
On dirait que la solution fournie par @ChrisVasseli est la meilleure. J'aimerais fournir la même solution en Objective-C car la question concerne Objective-C (voir les balises)
@interface InteractivePopGestureDelegate : NSObject <UIGestureRecognizerDelegate> @property (nonatomic, weak) UINavigationController *navigationController; @property (nonatomic, weak) id<UIGestureRecognizerDelegate> originalDelegate; @end @implementation InteractivePopGestureDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { if (self.navigationController.navigationBarHidden && self.navigationController.viewControllers.count > 1) { return YES; } else { return [self.originalDelegate gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch]; } } - (BOOL)respondsToSelector:(SEL)aSelector { if (aSelector == @selector(gestureRecognizer:shouldReceiveTouch:)) { return YES; } else { return [self.originalDelegate respondsToSelector:aSelector]; } } - (id)forwardingTargetForSelector:(SEL)aSelector { return self.originalDelegate; } @end @interface NavigationController () @property (nonatomic) InteractivePopGestureDelegate *interactivePopGestureDelegate; @end @implementation NavigationController - (void)viewDidLoad { [super viewDidLoad]; self.interactivePopGestureDelegate = [InteractivePopGestureDelegate new]; self.interactivePopGestureDelegate.navigationController = self; self.interactivePopGestureDelegate.originalDelegate = self.interactivePopGestureRecognizer.delegate; self.interactivePopGestureRecognizer.delegate = self.interactivePopGestureDelegate; } @end
la source
Ma solution est d'étendre directement la
UINavigationController
classe:import UIKit extension UINavigationController: UIGestureRecognizerDelegate { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.interactivePopGestureRecognizer?.delegate = self } public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return self.viewControllers.count > 1 } }
De cette façon, tous les contrôleurs de navigation pourront être supprimés en glissant.
la source
viewDidAppear
ignorer tous les appels sur les VC appartenant à n'importe quel contrôleur de navigation.Vous pouvez le faire avec un délégué proxy. Lorsque vous créez le contrôleur de navigation, saisissez le délégué existant. Et transmettez-le au proxy. Passez ensuite toutes les méthodes de délégué au délégué existant, sauf en
gestureRecognizer:shouldReceiveTouch:
utilisantforwardingTargetForSelector:
Installer:
let vc = UIViewController(nibName: nil, bundle: nil) let navVC = UINavigationController(rootViewController: vc) let bridgingDelegate = ProxyDelegate() bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegate
Délégué proxy:
class ProxyDelegate: NSObject, UIGestureRecognizerDelegate { var existingDelegate: UIGestureRecognizerDelegate? = nil override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? { return existingDelegate } func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool { return true } }
la source
La réponse de Hunter Monk est vraiment géniale, mais malheureusement dans iOS 13.3.1, cela ne fonctionne pas.
Je vais vous expliquer une autre façon de se cacher
UINavigationBar
et de ne pas perdreswipe to back gesture
. J'ai testé sur iOS 13.3.1 et 12.4.3 et cela fonctionne.Vous devez créer une classe personnalisée
UINavigationController
et définir cette classe pourUINavigationController
dansStoryboard
Ne cachez PAS
NavigationBar
leStoryboard
Exemple sur
Storyboard
:Et enfin, mettre ceci:
navigationBar.isHidden = true
dansviewDidLoad
deCustomNavigationController
classe.Assurez-vous de ne PAS utiliser cette méthode
setNavigationBarHidden(true, animated: true)
pour masquer le fichierNavigationBar
.import UIKit class CustomNavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() navigationBar.isHidden = true } }
la source
iOS 13.4.1
et faites glisser vers l'arrière fonctionne.Réponse Xamarin:
Implémentez l'
IUIGestureRecognizerDelegate
interface dans la définition de classe de votre ViewController:public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate
Dans votre ViewController, ajoutez la méthode suivante:
[Export("gestureRecognizerShouldBegin:")] public bool ShouldBegin(UIGestureRecognizer recognizer) { if (recognizer is UIScreenEdgePanGestureRecognizer && NavigationController.ViewControllers.Length == 1) { return false; } return true; }
Dans votre ViewController,
ViewDidLoad()
ajoutez la ligne suivante:NavigationController.InteractivePopGestureRecognizer.Delegate = this;
la source
UINavigationController
contrôleur de vue racine du? J'obtiens leEXEC_BAD_ACCESS
quand j'essaye ça.gestureRecognizerShouldBegin:
.J'ai essayé cela et cela fonctionne parfaitement: Comment masquer la barre de navigation sans perdre la capacité de retour en arrière
L'idée est d'implémenter "UIGestureRecognizerDelegate" dans votre .h et de l'ajouter à votre fichier .m.
- (void)viewWillAppear:(BOOL)animated { // hide nav bar [[self navigationController] setNavigationBarHidden:YES animated:YES]; // enable slide-back if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = YES; self.navigationController.interactivePopGestureRecognizer.delegate = self; } } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return YES; }
la source
Voici ma solution: je change alpha sur la barre de navigation, mais la barre de navigation n'est pas masquée. Tous mes contrôleurs de vue sont une sous-classe de mon BaseViewController, et là j'ai:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.navigationBar.alpha = 0.0 }
Vous pouvez également sous-classer UINavigationController et y placer cette méthode.
la source
Certaines personnes ont eu du succès en appelant la
setNavigationBarHidden
méthode avec animated à laYES
place.la source
Dans mon contrôleur de vue sans barre de navigation, j'utilise
open override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) CATransaction.begin() UIView.animate(withDuration: 0.25, animations: { [weak self] in self?.navigationController?.navigationBar.alpha = 0.01 }) CATransaction.commit() } open override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) CATransaction.begin() UIView.animate(withDuration: 0.25, animations: { [weak self] in self?.navigationController?.navigationBar.alpha = 1.0 }) CATransaction.commit() }
Pendant le rejet interactif, le bouton de retour brillera cependant, c'est pourquoi je l'ai caché.
la source
Il existe une solution vraiment simple que j'ai essayée et qui fonctionne parfaitement, c'est dans Xamarin.iOS mais peut également être appliquée en natif:
public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); this.NavigationController.SetNavigationBarHidden(true, true); } public override void ViewDidAppear(bool animated) { base.ViewDidAppear(animated); this.NavigationController.SetNavigationBarHidden(false, false); this.NavigationController.NavigationBar.Hidden = true; } public override void ViewWillDisappear(bool animated) { base.ViewWillDisappear(animated); this.NavigationController.SetNavigationBarHidden(true, false); }
la source
Voici comment désactiver la reconnaissance de gestes lorsque l'utilisateur glisse hors de ViewController. Vous pouvez le coller sur votre viewWillAppear () ou sur vos méthodes ViewDidLoad ().
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }
la source