Saya suka paket gesek yang diwarisi dari menyematkan pandangan Anda dalam file UINavigationController
. Sayangnya saya tidak dapat menemukan cara untuk menyembunyikannya NavigationBar
tetapi masih memiliki touch pan swipe back gesture
. Saya dapat menulis gerakan khusus tetapi saya memilih untuk tidak melakukannya dan sebagai gantinya mengandalkan UINavigationController
gesekan ke belakang gesture
.
jika saya hapus centang di storyboard, geser ke belakang tidak berfungsi
sebagai alternatif jika saya secara terprogram menyembunyikannya, skenario yang sama.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
Apakah tidak ada cara untuk menyembunyikan bagian atas NavigationBar
dan masih memiliki gesekan?
Jawaban:
Sebuah hack yang berhasil adalah mengatur
interactivePopGestureRecognizer
delegasiUINavigationController
menjadinil
seperti ini:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
Tetapi dalam beberapa situasi hal itu dapat menimbulkan efek aneh.
sumber
gestureRecognizerShouldBegin
, kembalitrue
jikanavigationController
'sviewController
hitung lebih besar dari 0viewWillDisappear
dan sejauh ini tidak mengalami efek samping yang merugikan.Masalah dengan Metode Lain
Pengaturan
interactivePopGestureRecognizer.delegate = nil
memiliki efek samping yang tidak diinginkan.Pengaturan
navigationController?.navigationBar.hidden = true
berfungsi, tetapi tidak memungkinkan perubahan Anda di bilah navigasi disembunyikan.Terakhir, umumnya praktik yang lebih baik adalah membuat objek model
UIGestureRecognizerDelegate
untuk pengontrol navigasi Anda. Menyetelnya ke pengontrol diUINavigationController
tumpukan inilah yang menyebabkanEXC_BAD_ACCESS
kesalahan.Solusi Lengkap
Pertama, tambahkan kelas ini ke proyek Anda:
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 } }
Kemudian, setel pengontrol navigasi Anda
interactivePopGestureRecognizer.delegate
ke instanceInteractivePopRecognizer
kelas baru Anda .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 }
Nikmati bilah navigasi tersembunyi tanpa efek samping, yang berfungsi bahkan jika pengontrol teratas Anda memiliki subview tampilan tabel, koleksi, atau tampilan gulir.
sumber
Dalam kasus saya, untuk mencegah efek aneh
Pengontrol tampilan root
override func viewDidLoad() { super.viewDidLoad() // Enable swipe back when no navigation bar navigationController?.interactivePopGestureRecognizer?.delegate = self } // UIGestureRecognizerDelegate func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let navVc = navigationController { return navVc.viewControllers.count > 1 } return false }
sumber
EXEC_BAD_ACCESS
UIGestureRecognizerDelegate
ke pengontrol tampilan root ... Dalam kasus saya, delegasi disetel ke nihil di pengontrol tampilan yang lebih baru daripada pengontrol tampilan root, jadi ketika kembali ke pengontrol tampilan root,gestureRecognizerShouldBegin
tidak dipanggil. Jadi saya menempatkannya.delegate = self
diviewDidAppear()
. Itu memecahkan efek aneh dalam kasusku .. Cheers!EXEC_BAD_ACCESS
terjadi?EXC_BAD_ACCESS
kesalahan: stackoverflow.com/questions/28746123/…Diperbarui untuk iOS 13.4
iOS 13.4 merusak solusi sebelumnya, jadi semuanya akan menjadi buruk. Sepertinya di iOS 13.4 perilaku ini sekarang dikontrol dengan metode privat
_gestureRecognizer:shouldReceiveEvent:
(jangan disamakan denganshouldReceive
metode publik baru yang ditambahkan di iOS 13.4).Saya menemukan bahwa solusi lain yang diposting mengesampingkan delegasi, atau menyetelnya ke nol menyebabkan beberapa perilaku yang tidak terduga.
Dalam kasus saya, ketika saya berada di atas tumpukan navigasi dan mencoba menggunakan isyarat untuk memunculkan satu lagi, itu akan gagal (seperti yang diharapkan), tetapi upaya selanjutnya untuk mendorong ke tumpukan akan mulai menyebabkan gangguan grafis aneh di bilah navigasi. Ini masuk akal, karena delegasi digunakan untuk menangani lebih dari sekedar apakah akan memblokir isyarat agar tidak dikenali saat bilah navigasi disembunyikan, dan semua perilaku lain itu dibuang.
Dari pengujian saya, tampaknya itu
gestureRecognizer(_:, shouldReceiveTouch:)
adalah metode yang diterapkan oleh delegasi asli untuk memblokir isyarat agar tidak dikenali saat bilah navigasi disembunyikan, bukangestureRecognizerShouldBegin(_:)
. Solusi lain yang diimplementasikangestureRecognizerShouldBegin(_:)
dalam pekerjaan delegasi mereka karena kurangnya implementasigestureRecognizer(_:, shouldReceiveTouch:)
akan menyebabkan perilaku default menerima semua sentuhan.Solusi @Nathan Perry semakin dekat, tetapi tanpa implementasi
respondsToSelector(_:)
, kode UIKit yang mengirimkan pesan ke delegasi akan percaya bahwa tidak ada implementasi untuk metode delegasi lainnya, danforwardingTargetForSelector(_:)
tidak akan pernah dipanggil.Jadi, kami mengontrol `gestureRecognizer (_ :, shouldReceiveTouch :) dalam satu skenario tertentu yang ingin kami ubah perilakunya, dan sebaliknya meneruskan yang lainnya ke delegasi.
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 } } }
sumber
gestureRecognizerShouldBegin:
hal itu, dan "tampaknya berhasil". Ingin tahu apakah saya harus berhati-hati.navigationController
merupakan referensi yang kuat di AlwaysPoppableDelegate. Saya telah mengedit kode untuk menjadikannya sebagaiweak
referensi.Anda dapat membuat subkelas UINavigationController sebagai berikut:
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @end
Penerapan:
@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
sumber
UIPageViewController
menggulung.Sederhana, tanpa efek samping. Jawaban
Meskipun sebagian besar jawaban di sini bagus, namun tampaknya memiliki efek samping yang tidak diinginkan (kerusakan aplikasi) atau bertele-tele.
Solusi paling sederhana namun fungsional yang dapat saya hasilkan adalah sebagai berikut:
Di ViewController tempat Anda menyembunyikan 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 } }
Jawaban lain menyarankan hanya menetapkan delegasi ke nol. Menggeser ke belakang ke pengontrol tampilan awal di tumpukan navigasi akan membuat semua gerakan dinonaktifkan. Semacam pengawasan, mungkin, dari pengembang UIKit / UIGesture.
Selain itu, beberapa jawaban di sini yang telah saya terapkan menghasilkan perilaku navigasi apel non-standar (khususnya, memungkinkan kemampuan untuk menggulir ke atas atau ke bawah sambil juga menggesek ke belakang). Jawaban ini juga tampak sedikit bertele-tele dan dalam beberapa kasus tidak lengkap.
sumber
viewDidLoad()
bukan tempat yang baik untuk menangkapinitialInteractivePopGestureRecognizerDelegate
karenanavigationController
mungkin nihil di sana (belum didorong ke tumpukan).viewWillAppear
dari tempat di mana Anda menyembunyikan bilah navigasi akan lebih sesuaiBerdasarkan jawaban Hunter Maximillion Monk , saya membuat subclass untuk UINavigationController dan kemudian mengatur kelas kustom untuk UINavigationController saya di storyboard saya. Kode akhir untuk kedua kelas terlihat seperti ini:
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) } }
Papan cerita:
sumber
Sepertinya solusi yang diberikan oleh @ChrisVasseli adalah yang terbaik. Saya ingin memberikan solusi yang sama di Objective-C karena pertanyaannya adalah tentang Objective-C (lihat tag)
@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
sumber
Solusi saya adalah dengan langsung memperluas
UINavigationController
kelas: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 } }
Dengan cara ini, semua pengontrol navigasi akan dapat ditutup dengan menggeser.
sumber
viewDidAppear
panggilan di VC milik pengontrol navigasi apa pun diabaikan.Anda dapat melakukannya dengan Proxy Delegate. Saat Anda membuat pengontrol navigasi, ambil delegasi yang ada. Dan berikan ke proxy. Kemudian teruskan semua metode delegasi ke delegasi yang ada kecuali
gestureRecognizer:shouldReceiveTouch:
menggunakanforwardingTargetForSelector:
Mendirikan:
let vc = UIViewController(nibName: nil, bundle: nil) let navVC = UINavigationController(rootViewController: vc) let bridgingDelegate = ProxyDelegate() bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegate
Delegasi Proksi:
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 } }
sumber
Jawaban Hunter Monk benar-benar luar biasa, tetapi sayangnya di iOS 13.3.1, itu tidak berfungsi.
Saya akan menjelaskan cara lain untuk bersembunyi
UINavigationBar
dan tidak kalahswipe to back gesture
. Saya telah menguji di iOS 13.3.1 dan 12.4.3 dan berhasil.Anda perlu membuat kelas khusus
UINavigationController
dan mengatur kelas itu untukUINavigationController
diStoryboard
JANGAN sembunyikan
NavigationBar
diStoryboard
Contoh di
Storyboard
:Dan akhirnya, masukkan ini:
navigationBar.isHidden = true
diviewDidLoad
dalamCustomNavigationController
kelas.Pastikan, JANGAN gunakan metode ini
setNavigationBarHidden(true, animated: true)
untuk menyembunyikan fileNavigationBar
.import UIKit class CustomNavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() navigationBar.isHidden = true } }
sumber
iOS 13.4.1
dan geser kembali berfungsi.Xamarin Jawaban:
Implementasikan
IUIGestureRecognizerDelegate
Antarmuka dalam definisi Kelas ViewController Anda:public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate
Di ViewController Anda, tambahkan metode berikut:
[Export("gestureRecognizerShouldBegin:")] public bool ShouldBegin(UIGestureRecognizer recognizer) { if (recognizer is UIScreenEdgePanGestureRecognizer && NavigationController.ViewControllers.Length == 1) { return false; } return true; }
Di ViewController Anda,
ViewDidLoad()
tambahkan baris berikut:NavigationController.InteractivePopGestureRecognizer.Delegate = this;
sumber
UINavigationController
pengontrol tampilan root? Saya mengertiEXEC_BAD_ACCESS
ketika saya mencoba ini.gestureRecognizerShouldBegin:
.Saya sudah mencoba ini dan berfungsi dengan sempurna: Cara menyembunyikan Bilah Navigasi tanpa kehilangan kemampuan geser ke belakang
Idenya adalah untuk mengimplementasikan "UIGestureRecognizerDelegate" di .h Anda dan menambahkan ini ke file .m Anda.
- (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; }
sumber
Inilah solusi saya: Saya mengubah alfa pada bilah navigasi, tetapi bilah navigasi tidak tersembunyi. Semua pengontrol tampilan saya adalah subkelas dari BaseViewController saya, dan di sana saya memiliki:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.navigationBar.alpha = 0.0 }
Anda juga bisa membuat subkelas UINavigationController dan meletakkan metode itu di sana.
sumber
Beberapa orang telah berhasil dengan memanggil
setNavigationBarHidden
metode animasiYES
sebagai gantinya.sumber
Dalam pengontrol tampilan saya tanpa bilah navigasi yang saya gunakan
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() }
Selama penutupan interaktif, tombol kembali akan bersinar, itulah sebabnya saya menyembunyikannya.
sumber
Ada solusi yang sangat sederhana yang saya coba dan berfungsi dengan sempurna, ini ada di Xamarin.iOS tetapi dapat diterapkan ke native juga:
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); }
sumber
Berikut adalah cara menonaktifkan pengenal de gestur saat pengguna keluar dari ViewController. Anda dapat menempelkannya di viewWillAppear () atau pada metode ViewDidLoad () Anda.
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }
sumber