Мне нравится пакет swipe, унаследованный от встраивания ваших представлений в файл UINavigationController
. К сожалению, я не могу найти способ скрыть, NavigationBar
но все еще прокрутите сенсорную панель назад gesture
. Я могу писать собственные жесты, но предпочитаю этого не делать и вместо этого полагаться на UINavigationController
смахивание назад gesture
.
если я сниму флажок в раскадровке, обратное смахивание не будет работать
в качестве альтернативы, если я программно скрою это, тот же сценарий.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
Нет возможности спрятать верхнюю часть NavigationBar
и по-прежнему смахивать?
Ответы:
Хак , который работает это установить
interactivePopGestureRecognizer
делегат «S изUINavigationController
кnil
так:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
Но в некоторых ситуациях это могло вызвать странные эффекты.
источник
gestureRecognizerShouldBegin
, возвращаясь ,true
еслиnavigationController
«sviewController
счета больше 0.viewWillDisappear
и до сих пор не испытало побочных эффектов.Проблемы с другими методами
Установка
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 }
Наслаждайтесь скрытой панелью навигации без побочных эффектов, которая работает, даже если на вашем верхнем контроллере есть подвиды таблиц, коллекций или прокрутки.
источник
В моем случае для предотвращения странных эффектов
Контроллер корневого представления
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/
источник
EXEC_BAD_ACCESS
UIGestureRecognizerDelegate
к корневому контроллеру представления ... В моем случае делегат был установлен в nil в более позднем контроллере представления, чем контроллер корневого представления, поэтому при возврате к контроллеру корневого представленияgestureRecognizerShouldBegin
он не был вызван. Таким образом , я поместил.delegate = self
вviewDidAppear()
. Это разрешило странные эффекты в моем случае .. Ура!EXEC_BAD_ACCESS
произойдет?EXC_BAD_ACCESS
ошибке: stackoverflow.com/questions/28746123/…Обновлено для iOS 13.4
iOS 13.4 сломала предыдущее решение, так что все будет ужасно. Похоже, что в iOS 13.4 это поведение теперь контролируется частным методом
_gestureRecognizer:shouldReceiveEvent:
(не путать с новым общедоступнымshouldReceive
методом, добавленным в iOS 13.4).Я обнаружил, что другие опубликованные решения, переопределяющие делегата или устанавливающие для него значение nil, вызывают некоторое неожиданное поведение.
В моем случае, когда я был на вершине стека навигации и попытался использовать этот жест, чтобы открыть еще один, он потерпел неудачу (как и ожидалось), но последующие попытки вставить в стек начали вызывать странные графические сбои в Панель навигации. Это имеет смысл, потому что делегат используется для обработки большего, чем просто вопрос о том, следует ли блокировать распознавание жеста, когда панель навигации скрыта, и все это другое поведение было выброшено.
Судя по моему тестированию, похоже, что
gestureRecognizer(_:, shouldReceiveTouch:)
это метод, который реализует исходный делегат для блокировки распознавания жеста, когда панель навигации скрыта, а неgestureRecognizerShouldBegin(_:)
. Другие решения, которые реализуютсяgestureRecognizerShouldBegin(_:)
в их делегированной работе, потому что отсутствие реализацииgestureRecognizer(_:, shouldReceiveTouch:)
вызовет поведение по умолчанию - получение всех касаний.Решение @Nathan Perry приближается, но без реализации
respondsToSelector(_:)
код UIKit, который отправляет сообщения делегату, будет считать, что нет реализации ни для одного из других методов делегата, иforwardingTargetForSelector(_:)
никогда не будет вызван.Итак, мы берем на себя управление gestureRecognizer (_ :, shouldReceiveTouch :) в одном конкретном сценарии, в котором мы хотим изменить поведение, а в противном случае пересылаем все остальное делегату.
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 } } }
источник
gestureRecognizerShouldBegin:
это, и это "похоже на работу". Интересно, стоит ли мне искать.navigationController
была сильная ссылка в AlwaysPoppableDelegate. Я отредактировал код, чтобы сделать егоweak
ссылкой.Вы можете создать подкласс 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
источник
UIPageViewController
прокрутку.Простой ответ без побочных эффектов
Хотя большинство ответов здесь хороши, они, по-видимому, имеют непредвиденные побочные эффекты (нарушение работы приложения) или являются подробными.
Самым простым, но функциональным решением, которое я смог придумать, было следующее:
В ViewController, в котором вы скрываете панель навигации,
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 } }
В других ответах предлагалось просто установить делегата на ноль. Переход назад к исходному контроллеру представления в стеке навигации приводит к отключению всех жестов. Возможно, какой-то надзор со стороны разработчиков UIKit / UIGesture.
Кроме того, некоторые ответы здесь, которые я реализовал, привели к нестандартному поведению навигации Apple (в частности, позволяя прокручивать вверх или вниз, а также прокручивать назад). Эти ответы также кажутся немного многословными и в некоторых случаях неполными.
источник
viewDidLoad()
это не лучшее место для захвата,initialInteractivePopGestureRecognizerDelegate
так как тамnavigationController
может быть ноль (еще не помещено в стек).viewWillAppear
места, где вы скрываете панель навигации, было бы более подходящимСтроительство из на ответе Хантера Максимиллиона Монка , я создал подкласс для 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) } }
Раскадровка:
источник
Похоже, решение, предоставленное @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
источник
Мое решение - напрямую расширить
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 } }
Таким образом, все контроллеры навигации будут отключены путем скольжения.
источник
viewDidAppear
к игнорированию всех вызовов VC, принадлежащих любому контроллеру навигации.Вы можете сделать это с помощью прокси-делегата. Когда вы создаете контроллер навигации, возьмите существующий делегат. И передаем в прокси. Затем передайте все методы делегата существующему делегату, кроме
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 } }
источник
Ответ Hunter Monk действительно потрясающий, но, к сожалению, в iOS 13.3.1 он не работает.
Я объясню еще один способ спрятаться
UINavigationBar
и не проигратьswipe to back gesture
. Я тестировал iOS 13.3.1 и 12.4.3, и все работает.Вам нужно создать собственный класс
UINavigationController
и установить этот классUINavigationController
вStoryboard
Не скрывайте
NavigationBar
наStoryboard
Пример на
Storyboard
:И, наконец, вставьте это:
navigationBar.isHidden = true
inviewDidLoad
ofCustomNavigationController
класса.Убедитесь, что НЕ используйте этот метод
setNavigationBarHidden(true, animated: true)
для скрытия файловNavigationBar
.import UIKit class CustomNavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() navigationBar.isHidden = true } }
источник
iOS 13.4.1
и смахивание назад работает.Ответ 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;
источник
UINavigationController
контроллере корневого представления? Я получаю,EXEC_BAD_ACCESS
когда пробую это.gestureRecognizerShouldBegin:
.Я пробовал это, и он отлично работает: как скрыть панель навигации, не теряя возможности скольжения
Идея состоит в том, чтобы реализовать «UIGestureRecognizerDelegate» в вашем .h и добавить его в ваш .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; }
источник
Вот мое решение: я меняю альфа-канал на панели навигации, но панель навигации не скрывается. Все мои контроллеры представления являются подклассом моего BaseViewController, и у меня есть:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.navigationBar.alpha = 0.0 }
Вы также можете создать подкласс UINavigationController и поместить туда этот метод.
источник
Некоторым людям удалось вместо этого вызвать
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() }
Во время интерактивного увольнения кнопка назад будет светиться, поэтому я ее спрятал.
источник
Есть действительно простое решение, которое я пробовал и отлично работает, оно есть в 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); }
источник
Вот как отключить распознаватель жестов, когда пользователь выходит из ViewController. Вы можете вставить его в свой viewWillAppear () или в свои методы ViewDidLoad ().
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }
источник