Eu amo o pacote de furto que é herdado da incorporação de suas visualizações em um UINavigationController
. Infelizmente, não consigo encontrar uma maneira de ocultar o, NavigationBar
mas ainda tenho o toque de deslizar para trás gesture
. Posso escrever gestos personalizados, mas prefiro não fazê- UINavigationController
lo e, em gesture
vez disso, preferir deslizar para trás .
se eu desmarcar no storyboard, deslizar para trás não funciona
alternativamente, se eu ocultar programaticamente, o mesmo cenário.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
Não há como ocultar a parte superior NavigationBar
e ainda deslizar?
Respostas:
Um hack que está funcionando é definir o
interactivePopGestureRecognizer
delegado deUINavigationController
paranil
assim:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
Mas em algumas situações, pode criar efeitos estranhos.
fonte
gestureRecognizerShouldBegin
, retornandotrue
se onavigationController
'sviewController
contagem é maior que 0.viewWillDisappear
e, até agora, não experimentou nenhum efeito colateral adverso.Problemas com outros métodos
Configurando o
interactivePopGestureRecognizer.delegate = nil
tem efeitos colaterais indesejados.A configuração
navigationController?.navigationBar.hidden = true
funciona, mas não permite que sua alteração na barra de navegação seja ocultada.Por último, geralmente é uma prática melhor criar um objeto de modelo que seja o
UIGestureRecognizerDelegate
para seu controlador de navegação. Configurá-lo como um controlador naUINavigationController
pilha é o que está causando osEXC_BAD_ACCESS
erros.Solução Completa
Primeiro, adicione esta classe ao seu projeto:
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 } }
Em seguida, defina seu controlador de navegação
interactivePopGestureRecognizer.delegate
para uma instância de sua novaInteractivePopRecognizer
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 }
Desfrute de uma barra de navegação oculta sem efeitos colaterais, que funciona mesmo se seu controlador superior tiver subvisualizações de tabela, coleção ou visualização de rolagem.
fonte
No meu caso, para evitar efeitos estranhos
Controlador de visualização raiz
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/
fonte
EXEC_BAD_ACCESS
UIGestureRecognizerDelegate
ao controlador de exibição raiz ... No meu caso, o delegado foi definido como nulo em um controlador de exibição posterior do que o controlador de exibição raiz, portanto, quando retornou ao controlador de exibição raiz,gestureRecognizerShouldBegin
não foi chamado. Então eu coloquei o.delegate = self
noviewDidAppear()
. Isso resolveu os estranhos efeitos no meu caso ... Saúde!EXEC_BAD_ACCESS
acontece?EXC_BAD_ACCESS
erro: stackoverflow.com/questions/28746123/…Atualizado para iOS 13.4
O iOS 13.4 quebrou a solução anterior, então as coisas vão ficar feias. Parece que no iOS 13.4 esse comportamento agora é controlado por um método privado
_gestureRecognizer:shouldReceiveEvent:
(não deve ser confundido com o novoshouldReceive
método público adicionado no iOS 13.4).Descobri que outras soluções postadas substituindo o delegado ou definindo-o como nulo causaram algum comportamento inesperado.
No meu caso, quando eu estava no topo da pilha de navegação e tentava usar o gesto para abrir mais uma, ele falharia (como esperado), mas as tentativas subsequentes de empurrar para a pilha começariam a causar falhas gráficas estranhas no Barra de navegação. Isso faz sentido, porque o delegado está sendo usado para lidar com mais do que apenas bloquear ou não o gesto de ser reconhecido quando a barra de navegação está oculta e todos os outros comportamentos foram descartados.
Pelo meu teste, parece que
gestureRecognizer(_:, shouldReceiveTouch:)
é o método que o delegado original está implementando para bloquear o gesto de ser reconhecido quando a barra de navegação está oculta, nãogestureRecognizerShouldBegin(_:)
. Outras soluções que implementamgestureRecognizerShouldBegin(_:)
em seus delegados funcionam porque a falta de uma implementação degestureRecognizer(_:, shouldReceiveTouch:)
causará o comportamento padrão de receber todos os toques.A solução de @Nathan Perry chega perto, mas sem uma implementação
respondsToSelector(_:)
, o código UIKit que envia mensagens para o delegado acreditará que não há implementação para nenhum dos outros métodos delegados eforwardingTargetForSelector(_:)
nunca será chamado.Portanto, assumimos o controle de `gestorRecognizer (_ :, deveriaReceberTouch :) em um cenário específico que desejamos modificar o comportamento e, caso contrário, encaminharemos todo o resto para o delegado.
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 } } }
fonte
gestureRecognizerShouldBegin:
coisa, e "parece funcionar". Me perguntando se eu deveria cuidar.navigationController
Ocorreu um vazamento de memória, pois era uma referência forte no AlwaysPoppableDelegate. Eu editei o código para fazer disso umaweak
referência.Você pode criar uma subclasse de UINavigationController da seguinte forma:
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @end
Implementação:
@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
fonte
UIPageViewController
overscroll.Resposta simples, sem efeito colateral
Embora a maioria das respostas aqui sejam boas, elas aparentemente têm efeitos colaterais indesejados (quebra de aplicativo) ou são prolixas.
A solução mais simples, porém funcional, que consegui propor foi a seguinte:
No ViewController em que você está ocultando a barra de navegação,
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 } }
Outras respostas sugeriram simplesmente definir o delegado como zero. Deslizar para trás para o controlador de visualização inicial na pilha de navegação resulta na desativação de todos os gestos. Algum tipo de descuido, talvez, dos desenvolvedores de UIKit / UIGesture.
Além disso, algumas respostas aqui que implementei resultaram em um comportamento de navegação fora do padrão da apple (especificamente, permitindo a capacidade de rolar para cima ou para baixo enquanto deslize para trás). Essas respostas também parecem um pouco prolixas e, em alguns casos, incompletas.
fonte
viewDidLoad()
não é um bom lugar para capturar,initialInteractivePopGestureRecognizerDelegate
poisnavigationController
pode haver nulo (ainda não colocado na pilha).viewWillAppear
do lugar onde você está escondendo a barra de navegação seria mais apropriadoCom base na resposta de Hunter Maximillion Monk , criei uma subclasse para UINavigationController e, em seguida, configurei a classe personalizada para meu UINavigationController em meu storyboard. O código final para as duas classes se parece com este:
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:
fonte
Parece que a solução fornecida por @ChrisVasseli é a melhor. Eu gostaria de fornecer a mesma solução em Objective-C porque a questão é sobre Objective-C (veja as tags)
@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
fonte
Minha solução é estender a
UINavigationController
classe diretamente :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 } }
Dessa forma, todos os controladores de navegação poderão ser dispensados deslizando-se.
fonte
viewDidAppear
chamadas nos VCs pertencentes a qualquer controlador de navegação sejam ignoradas.Você pode fazer isso com um delegado proxy. Quando você estiver construindo o controlador de navegação, pegue o delegado existente. E passe para o proxy. Em seguida, passe todos os métodos de delegado para o delegado existente, exceto
gestureRecognizer:shouldReceiveTouch:
usandoforwardingTargetForSelector:
Configuração:
let vc = UIViewController(nibName: nil, bundle: nil) let navVC = UINavigationController(rootViewController: vc) let bridgingDelegate = ProxyDelegate() bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegate
Delegado 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 } }
fonte
A resposta de Hunter Monk é realmente incrível, mas infelizmente no iOS 13.3.1, ela não funciona.
Vou explicar outra forma de se esconder
UINavigationBar
e não perderswipe to back gesture
. Eu testei no iOS 13.3.1 e 12.4.3 e funciona.Você precisa criar uma classe personalizada
UINavigationController
e definir essa classe paraUINavigationController
emStoryboard
NÃO esconda o
NavigationBar
noStoryboard
Exemplo em
Storyboard
:E, finalmente, coloque o seguinte:
navigationBar.isHidden = true
em salaviewDidLoad
deCustomNavigationController
aula.Certifique-se de NÃO usar este método
setNavigationBarHidden(true, animated: true)
para ocultar oNavigationBar
.import UIKit class CustomNavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() navigationBar.isHidden = true } }
fonte
iOS 13.4.1
e retrocesso funciona.Resposta de Xamarin:
Implemente a
IUIGestureRecognizerDelegate
interface na definição de classe do seu ViewController:public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate
Em seu ViewController, adicione o seguinte método:
[Export("gestureRecognizerShouldBegin:")] public bool ShouldBegin(UIGestureRecognizer recognizer) { if (recognizer is UIScreenEdgePanGestureRecognizer && NavigationController.ViewControllers.Length == 1) { return false; } return true; }
No seu ViewController,
ViewDidLoad()
adicione a seguinte linha:NavigationController.InteractivePopGestureRecognizer.Delegate = this;
fonte
UINavigationController
controlador de exibição de raiz do? Eu entendoEXEC_BAD_ACCESS
quando tento isso.gestureRecognizerShouldBegin:
.Eu tentei isso e está funcionando perfeitamente: Como ocultar a barra de navegação sem perder a capacidade de deslizar para trás
A ideia é implementar "UIGestureRecognizerDelegate" em seu .h e adicioná-lo ao seu arquivo .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; }
fonte
Aqui está minha solução: estou alterando o alfa na barra de navegação, mas a barra de navegação não está oculta. Todos os meus controladores de visualização são uma subclasse do meu BaseViewController, e aí eu tenho:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.navigationBar.alpha = 0.0 }
Você também pode criar uma subclasse de UINavigationController e colocar esse método lá.
fonte
Algumas pessoas tiveram sucesso ao chamar o
setNavigationBarHidden
método de animadoYES
.fonte
Na minha visão, controlador sem barra de navegação, eu uso
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() }
No entanto, durante a dispensa interativa, o botão Voltar irá brilhar, e é por isso que o escondi.
fonte
Existe uma solução realmente simples que experimentei e funciona perfeitamente, está no Xamarin.iOS, mas também pode ser aplicada ao nativo:
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); }
fonte
Aqui está como desativar o reconhecedor de gestos quando o usuário desliza para fora do ViewController. Você pode colá-lo em seus métodos viewWillAppear () ou ViewDidLoad ().
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }
fonte