Uwielbiam pakiet do przesuwania, który jest odziedziczony po osadzaniu widoków w pliku UINavigationController
. Niestety nie mogę znaleźć sposobu, aby ukryć, NavigationBar
ale nadal mam przesuwanie panelu dotykowego z powrotem gesture
. Mogę pisać niestandardowe gesty, ale wolę tego nie UINavigationController
robić i gesture
zamiast tego polegać na przesunięciu w tył .
jeśli odznaczę to w scenorysie, przesunięcie w tył nie działa
alternatywnie, jeśli programowo to ukryję, ten sam scenariusz.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
Czy nie ma sposobu, aby ukryć górę NavigationBar
i nadal mieć machnięcie?
Odpowiedzi:
Hack, który działa, polega na ustawieniu
interactivePopGestureRecognizer
delegata w następującyUINavigationController
sposóbnil
:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
Ale w niektórych sytuacjach może to wywołać dziwne efekty.
źródło
gestureRecognizerShouldBegin
, wracająctrue
jeślinavigationController
jestviewController
liczba jest większa niż 0.viewWillDisappear
i jak dotąd nie wystąpiły żadne niepożądane skutki uboczne.Problemy z innymi metodami
Ustawienie
interactivePopGestureRecognizer.delegate = nil
ma niezamierzone skutki uboczne.Ustawienie
navigationController?.navigationBar.hidden = true
działa, ale nie pozwala na ukrycie zmian w pasku nawigacji.Na koniec ogólnie lepszą praktyką jest utworzenie obiektu modelu, który jest
UIGestureRecognizerDelegate
przeznaczony dla kontrolera nawigacji. Ustawienie go na kontroler wUINavigationController
stosie powodujeEXC_BAD_ACCESS
błędy.Pełne rozwiązanie
Najpierw dodaj tę klasę do swojego projektu:
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 } }
Następnie ustaw kontroler nawigacji na
interactivePopGestureRecognizer.delegate
wystąpienie nowejInteractivePopRecognizer
klasy.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 }
Ciesz się ukrytym paskiem nawigacji bez efektów ubocznych, który działa nawet wtedy, gdy Twój górny kontroler ma podglądy tabeli, kolekcji lub przewijania.
źródło
W moim przypadku, aby zapobiec dziwnym efektom
Główny kontroler widoku
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/
źródło
EXEC_BAD_ACCESS
UIGestureRecognizerDelegate
do głównego kontrolera widoku ... W moim przypadku delegat został ustawiony na zero w późniejszym kontrolerze widoku niż główny kontroler widoku, więc po powrocie do głównego kontrolera widokugestureRecognizerShouldBegin
nie został wywołany. Więc umieściłem.delegate = self
wviewDidAppear()
. To rozwiązało dziwne efekty w moim przypadku… Na zdrowie!EXEC_BAD_ACCESS
stanie?EXC_BAD_ACCESS
błędzie: stackoverflow.com/questions/28746123/…Zaktualizowano dla iOS 13.4
iOS 13.4 zepsuł poprzednie rozwiązanie, więc sprawy potoczą się brzydko. Wygląda na to, że w iOS 13.4 to zachowanie jest teraz kontrolowane przez metodę prywatną
_gestureRecognizer:shouldReceiveEvent:
(nie mylić z nowąshouldReceive
metodą publiczną dodaną w iOS 13.4).Okazało się, że inne opublikowane rozwiązania zastępujące delegata lub ustawienie go na zero spowodowały nieoczekiwane zachowanie.
W moim przypadku, gdy byłem na szczycie stosu nawigacyjnego i próbowałem użyć gestu, aby wyskoczyć jeszcze jeden, kończyło się to niepowodzeniem (zgodnie z oczekiwaniami), ale kolejne próby wepchnięcia na stos zaczęłyby powodować dziwne graficzne usterki w Pasek nawigacyjny. Ma to sens, ponieważ delegat jest używany do obsługi czegoś więcej niż tylko tego, czy blokować rozpoznawanie gestu, gdy pasek nawigacji jest ukryty, a wszystkie inne zachowania były odrzucane.
Z moich testów wynika, że
gestureRecognizer(_:, shouldReceiveTouch:)
jest to metoda, którą implementuje oryginalny delegat, aby zablokować rozpoznawanie gestu, gdy pasek nawigacji jest ukryty, a niegestureRecognizerShouldBegin(_:)
. Inne rozwiązania, które zaimplementujągestureRecognizerShouldBegin(_:)
w swojej pracy delegata, ponieważ brak implementacjigestureRecognizer(_:, shouldReceiveTouch:)
spowoduje domyślne zachowanie wszystkich dotknięć.Rozwiązanie @Nathana Perry'ego zbliża się, ale bez implementacji
respondsToSelector(_:)
kod UIKit, który wysyła wiadomości do delegata, będzie uważał, że nie ma implementacji dla żadnej innej metody delegata iforwardingTargetForSelector(_:)
nigdy nie zostanie wywołany.Więc przejmujemy kontrolę nad `gestRecognizer (_ :, shouldReceiveTouch :) w jednym konkretnym scenariuszu, w którym chcemy zmodyfikować zachowanie, iw przeciwnym razie przekazujemy wszystko inne do delegata.
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 } } }
źródło
gestureRecognizerShouldBegin:
, co „wydaje się działać”. Zastanawiam się, czy powinienem uważać.navigationController
był silnym odniesieniem w AlwaysPoppableDelegate. Edytowałem kod, aby uczynić goweak
odniesieniem.Możesz podklasę UINavigationController w następujący sposób:
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @end
Realizacja:
@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
źródło
UIPageViewController
przewijaniu.Prosta odpowiedź bez skutków ubocznych
Chociaż większość odpowiedzi tutaj jest dobra, pozornie mają one niezamierzone skutki uboczne (zepsute aplikacje) lub są szczegółowe.
Najprostszym, ale funkcjonalnym rozwiązaniem, jakie mogłem wymyślić, było:
W kontrolerze ViewController, w którym ukrywasz pasek nawigacji,
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 } }
Inne odpowiedzi sugerowały jedynie ustawienie delegata na zero. Przeciągnięcie wstecz do początkowego kontrolera widoku na stosie nawigacyjnym powoduje wyłączenie wszystkich gestów. Być może jakieś niedopatrzenie twórców UIKit / UIGesture.
Ponadto niektóre odpowiedzi, które zaimplementowałem, zaowocowały niestandardowym zachowaniem nawigacji w jabłkach (w szczególności umożliwiając przewijanie w górę lub w dół, jednocześnie przesuwając palcem do tyłu). Te odpowiedzi również wydają się nieco rozwlekłe, a w niektórych przypadkach niepełne.
źródło
viewDidLoad()
nie jest dobrym miejscem do bicia,initialInteractivePopGestureRecognizerDelegate
ponieważnavigationController
może tam być zero (jeszcze nie trafiono na stos).viewWillAppear
miejsca, w którym ukrywasz pasek nawigacji, byłoby bardziej odpowiednieOpierając się na odpowiedzi Huntera Maximillion Monk , utworzyłem podklasę dla UINavigationController, a następnie ustawiłem niestandardową klasę dla mojego UINavigationController w moim storyboardzie. Ostateczny kod obu klas wygląda następująco:
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:
źródło
Wygląda na to, że rozwiązanie dostarczone przez @ChrisVasseli jest najlepsze. Chciałbym zapewnić to samo rozwiązanie w Objective-C, ponieważ pytanie dotyczy Objective-C (zobacz tagi)
@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
źródło
Moje rozwiązanie to bezpośrednie rozszerzenie
UINavigationController
klasy: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 } }
W ten sposób wszystkie kontrolery nawigacyjne zostaną odrzucone przez przesuwanie.
źródło
viewDidAppear
to ignorowanie wszystkich wywołań VC należących do dowolnego kontrolera nawigacyjnego.Możesz to zrobić za pomocą pełnomocnika proxy. Podczas tworzenia kontrolera nawigacji pobierz istniejącego delegata. I przekaż go do proxy. Następnie przekaż wszystkie metody delegata do istniejącego delegata, z wyjątkiem
gestureRecognizer:shouldReceiveTouch:
usingforwardingTargetForSelector:
Ustawiać:
let vc = UIViewController(nibName: nil, bundle: nil) let navVC = UINavigationController(rootViewController: vc) let bridgingDelegate = ProxyDelegate() bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegate
Pełnomocnik:
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 } }
źródło
Odpowiedź Hunter Monk jest naprawdę świetna, ale niestety w iOS 13.3.1 nie działa.
Wyjaśnię inny sposób, aby się ukryć
UINavigationBar
i nie zgubićswipe to back gesture
. Testowałem na iOS 13.3.1 i 12.4.3 i działa.Trzeba utworzyć klasy niestandardowej
UINavigationController
i ustawić dla tej klasyUINavigationController
wStoryboard
NIE ukrywaj
NavigationBar
naStoryboard
Przykład na
Storyboard
:I na koniec umieść to:
navigationBar.isHidden = true
inviewDidLoad
ofCustomNavigationController
class.Upewnij się, że NIE używaj tej metody
setNavigationBarHidden(true, animated: true)
do ukrywania plikuNavigationBar
.import UIKit class CustomNavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() navigationBar.isHidden = true } }
źródło
iOS 13.4.1
i przesuń palcem wstecz działa.Odpowiedź platformy Xamarin:
Zaimplementuj
IUIGestureRecognizerDelegate
interfejs w definicji klasy ViewControllera:public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate
W swoim ViewController dodaj następującą metodę:
[Export("gestureRecognizerShouldBegin:")] public bool ShouldBegin(UIGestureRecognizer recognizer) { if (recognizer is UIScreenEdgePanGestureRecognizer && NavigationController.ViewControllers.Length == 1) { return false; } return true; }
W kontrolerze ViewControllera
ViewDidLoad()
dodaj następujący wiersz:NavigationController.InteractivePopGestureRecognizer.Delegate = this;
źródło
UINavigationController
głównym kontrolerze widoku? Dostaję,EXEC_BAD_ACCESS
kiedy tego próbuję.gestureRecognizerShouldBegin:
.Wypróbowałem to i działa idealnie: jak ukryć pasek nawigacji bez utraty możliwości cofania
Chodzi o to, aby zaimplementować „UIGestureRecognizerDelegate” w swoim .h i dodać to do swojego pliku .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; }
źródło
Oto moje rozwiązanie: zmieniam alfę na pasku nawigacji, ale pasek nawigacji nie jest ukryty. Wszystkie moje kontrolery widoku są podklasą mojego BaseViewController i mam:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.navigationBar.alpha = 0.0 }
Możesz również utworzyć podklasę UINavigationController i umieścić tam tę metodę.
źródło
Niektórzy odnieśli sukces, nazywając
setNavigationBarHidden
metodęYES
zamiast tego animated .źródło
W moim widoku kontroler bez paska nawigacyjnego używam
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() }
Podczas interaktywnej zwalniania przycisk powrotu będzie jednak przeświecał, dlatego go ukryłem.
źródło
Jest naprawdę proste rozwiązanie, które wypróbowałem i działa idealnie, jest w Xamarin.iOS, ale można je również zastosować do natywnych:
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); }
źródło
Oto jak wyłączyć rozpoznawanie gestów, gdy użytkownik wysuwa się z ViewController. Możesz wkleić go do swojej metody viewWillAppear () lub do metod ViewDidLoad ().
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }
źródło