Нет проведите пальцем назад при скрытии панели навигации в UINavigationController
Мне нравится пакет салфетки, который унаследован от внедрения Ваших представлений в UINavigationController. К сожалению, я не могу найти способ скрыть панель навигации, но все еще имею жест возврата сенсорного панорамирования. Я могу писать пользовательские жесты, но я предпочитаю не делать этого и полагаться на жест UINavigationController back swipe.
если я сниму его в раскадровке, задний свайп не работает
кроме того, если я программно скрыть это тот же сценарий.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
нет ли способа скрыть верхнюю панель навигации и все еще иметь салфетки?
15 ответов:
хак, который работает, чтобы установить
interactivePopGestureRecognizer
'ы делегатUINavigationController
доnil
такой:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
но в некоторых ситуациях это может создать странные эффекты.
в моем случае, чтобы предотвратить странные эффекты
корневой контроллер вида
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/
проблемы с другими методами
задание
interactivePopGestureRecognizer.delegate = nil
имеет побочных эффектов.задание
navigationController?.navigationBar.hidden = true
работает, но не позволяет скрыть изменения в панели навигации.наконец, как правило, лучше создать объект модели, который является
UIGestureRecognizerDelegate
для контроллера навигации. Установка его на контроллер вUINavigationController
стек-это то, что вызываетEXC_BAD_ACCESS
ошибки.полный Решение
во-первых, добавьте этот класс в свой проект:
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 } }
затем установите навигационный контроллер
interactivePopGestureRecognizer.delegate
к экземпляру вашего новогоInteractivePopRecognizer
класса.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 }
наслаждайтесь скрытой навигационной панелью без каких-либо побочных эффектов, которая работает, даже если ваш верхний контроллер имеет таблицу, коллекцию или просмотр прокрутки.
Вы можете подкласс UINavigationController следующим образом:
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @end
реализация:
@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
строительство ответ охотника Максимилиана Монка, Я сделал подкласс для UINavigationController, а затем установил пользовательский класс для моего UINavigationController в моей раскадровке. Окончательный код для двух классов выглядит так:
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) } }
раскадровка:
я обнаружил, что другие опубликованные решения, переопределяющие делегат, или устанавливающие его на ноль, вызвали неожиданное поведение.
в моем случае, когда я был на вершине навигационного стека и попытался использовать жест, чтобы вытащить еще один, он потерпит неудачу (как и ожидалось), но последующие попытки нажать на стек начнут вызывать странные графические сбои в панели навигации. Это имеет смысл, потому что делегат используется для обработки больше, чем просто для блокировки жест от того, чтобы быть распознанным, когда панель навигации скрыта, и все это другое поведение было выброшено.
из моего тестирования, похоже, что
gestureRecognizer(_:, shouldReceiveTouch:)
- Это метод, который реализует исходный делегат, чтобы заблокировать распознавание жеста при скрытой панели навигации, а неgestureRecognizerShouldBegin(_:)
. Другие решения, которые реализуютgestureRecognizerShouldBegin(_:)
в их Делегатской работе из-за отсутствия реализацииgestureRecognizer(_:, shouldReceiveTouch:)
вызовет поведение по умолчанию получения всех прикосновений.решение@Nathan Perry приближается, но без реализации
respondsToSelector(_:)
, код UIKit, который отправляет сообщения делегату, будет считать, что нет реализации для любого из других методов делегата, иforwardingTargetForSelector(_:)
никогда не будет вызван.Итак, мы берем под свой контроль `gestureRecognizer (_:, shouldReceiveTouch:) в одном конкретном сценарии мы хотим изменить поведение, а в противном случае переслать все остальное делегату.
import Foundation 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? var originalDelegate: UIGestureRecognizerDelegate init(navigationController: AlwaysPoppableNavigationController, originalDelegate: UIGestureRecognizerDelegate) { self.navigationController = navigationController self.originalDelegate = originalDelegate } @objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if let nav = navigationController, nav.isNavigationBarHidden && nav.viewControllers.count > 1 { { return true } else { return self.originalDelegate.gestureRecognizer!(gestureRecognizer, shouldReceive: touch) } } override func responds(to aSelector: Selector) -> Bool { if aSelector == #selector(gestureRecognizer(_:shouldReceive:)) { return true } else { return self.originalDelegate.responds(to: aSelector) } } override func forwardingTarget(for aSelector: Selector!) -> Any? { return self.originalDelegate } }
похоже, что решение, предоставленное @ChrisVasseli, является лучшим. Я хотел бы предоставить такое же решение в Objective-C, потому что вопрос касается Objective-C (см. теги)
@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
вы можете сделать это с помощью прокси-делегат. При создании навигационного контроллера возьмите существующий делегат. И передайте его в прокси. Затем передайте все методы делегата существующему делегату, кроме
gestureRecognizer:shouldReceiveTouch:
используяforwardingTargetForSelector:
настройка:
let vc = UIViewController(nibName: nil, bundle: nil) let navVC = UINavigationController(rootViewController: vc) let bridgingDelegate = ProxyDelegate() bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegate
Прокси-Делегат:
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 } }
Xamarin В Ответ:
реализовать
IUIGestureRecognizerDelegate
интерфейс в определении класса вашего ViewController:public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate
в вашем ViewController добавьте следующий метод:
[Export("gestureRecognizerShouldBegin:")] public bool ShouldBegin(UIGestureRecognizer recognizer) { if (recognizer is UIScreenEdgePanGestureRecognizer && NavigationController.ViewControllers.Length == 1) { return false; } return true; }
в вашем ViewController
ViewDidLoad()
добавить следующую строку :NavigationController.InteractivePopGestureRecognizer.Delegate = this;
Я пробовал это, и это работает отлично : Как скрыть панель навигации без потери слайд-возможности
идея заключается в реализации "UIGestureRecognizerDelegate" в вашем .ч и добавьте это к своему .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; }
некоторые люди имели успех, вызвав
setNavigationBarHidden
метод с анимированнымиYES
вместо.
в моем представлении контроллер без навигационной панели я использую
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() }
во время интерактивного увольнения кнопка "Назад" будет просвечивать, хотя, поэтому я ее спрятал.
мое решение заключается в прямом расширении
UINavigationController
класс :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 } }
таким образом, все навигационные контроллеры будут отклоняться путем скольжения.
есть очень простое решение, которое я пробовал и работает отлично, это в Xamarin.iOS, но может быть применен и к родному:
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); }
вот как отключить распознаватель жестов de, когда пользователь выскальзывает из ViewController. Вы можете вставить его в свой метод viewWillAppear() или ViewDidLoad ().
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }