Saya perlu melakukan beberapa tindakan ketika tombol kembali (kembali ke layar sebelumnya, kembali ke tampilan orang tua) ditekan di Navbar.
Apakah ada beberapa metode yang dapat saya terapkan untuk menangkap peristiwa dan menjalankan beberapa tindakan untuk menjeda dan menyimpan data sebelum layar menghilang?
iphone
objective-c
ios
xcode
ewok
sumber
sumber
Jawaban:
PEMBARUAN: Menurut beberapa komentar, solusi dalam jawaban asli tampaknya tidak berfungsi dalam skenario tertentu di iOS 8+. Saya tidak dapat memverifikasi bahwa itu sebenarnya terjadi tanpa rincian lebih lanjut.
Namun bagi Anda dalam situasi itu ada alternatif. Mendeteksi saat pengontrol tampilan dimunculkan dimungkinkan dengan mengganti
willMove(toParentViewController:)
. Ide dasarnya adalah bahwa pengontrol tampilan akan muncul saatparent
itunil
.Lihat "Menerapkan Pengontrol Tampilan Penampung" untuk detail lebih lanjut.
Sejak iOS 5, saya telah menemukan bahwa cara termudah untuk menangani situasi ini adalah menggunakan metode baru
- (BOOL)isMovingFromParentViewController
:- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.isMovingFromParentViewController) { // Do your stuff here } }
- (BOOL)isMovingFromParentViewController
masuk akal saat Anda mendorong dan memunculkan pengontrol dalam tumpukan navigasi.Namun, jika Anda menyajikan pengontrol tampilan modal, Anda harus menggunakan
- (BOOL)isBeingDismissed
sebagai gantinya:- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.isBeingDismissed) { // Do your stuff here } }
Seperti disebutkan dalam pertanyaan ini , Anda dapat menggabungkan kedua properti:
- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.isMovingFromParentViewController || self.isBeingDismissed) { // Do your stuff here } }
Solusi lain bergantung pada keberadaan a
UINavigationBar
. Alih-alih menyukai pendekatan saya lebih karena itu memisahkan tugas yang diperlukan untuk melakukan dari tindakan yang memicu acara, yaitu menekan tombol kembali.sumber
self.isMovingFromParentViewController
memiliki nilai TRUE ketika saya memunculkan tumpukan navigasi secara terprogram menggunakanpopToRootViewControllerAnimated
- tanpa sentuhan pada tombol kembali. Haruskah saya meremehkan jawaban Anda? (subjek mengatakan "tombol 'kembali' ditekan di bilah navigasi")override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController(){ println("back button pressed") } }
-viewDidDisappear:
karena mungkin saja Anda akan mendapatkan-viewWillDisappear:
tanpa-viewDidDisappear:
(seperti saat Anda mulai menggesek untuk menutup item pengontrol navigasi dan kemudian membatalkan gesekan itu.Sementara
viewWillAppear()
danviewDidDisappear()
yang disebut ketika tombol kembali disadap, mereka juga disebut di lain waktu. Lihat akhir jawaban untuk lebih lanjut tentang itu.Menggunakan UIViewController.parent
Mendeteksi tombol kembali lebih baik dilakukan ketika VC dihapus dari induknya (NavigationController) dengan bantuan
willMoveToParentViewController(_:)
ORdidMoveToParentViewController()
Jika induknya nihil, pengontrol tampilan akan dikeluarkan dari tumpukan navigasi dan ditutup. Jika induk bukan nol, itu akan ditambahkan ke tumpukan dan disajikan.
// Objective-C -(void)willMoveToParentViewController:(UIViewController *)parent { [super willMoveToParentViewController:parent]; if (!parent){ // The back button was pressed or interactive gesture used } } // Swift override func willMove(toParent parent: UIViewController?) { super.willMove(toParent: parent) if parent == nil { // The back button was pressed or interactive gesture used } }
Swap keluar
willMove
untukdidMove
dan cek self.parent untuk melakukan pekerjaan setelah pengontrol tampilan diberhentikan.Menghentikan penutupan
Perhatikan, memeriksa induk tidak memungkinkan Anda untuk "menjeda" transisi jika Anda perlu melakukan semacam penyimpanan asinkron. Untuk melakukan itu, Anda dapat menerapkan yang berikut ini. Satunya downside di sini adalah Anda kehilangan tombol kembali bergaya / animasi iOS yang mewah. Berhati-hatilah juga di sini dengan gerakan gesek interaktif. Gunakan yang berikut untuk menangani kasus ini.
var backButton : UIBarButtonItem! override func viewDidLoad() { super.viewDidLoad() // Disable the swipe to make sure you get your chance to save self.navigationController?.interactivePopGestureRecognizer.enabled = false // Replace the default back button self.navigationItem.setHidesBackButton(true, animated: false) self.backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "goBack") self.navigationItem.leftBarButtonItem = backButton } // Then handle the button selection func goBack() { // Here we just remove the back button, you could also disabled it or better yet show an activityIndicator self.navigationItem.leftBarButtonItem = nil someData.saveInBackground { (success, error) -> Void in if success { self.navigationController?.popViewControllerAnimated(true) // Don't forget to re-enable the interactive gesture self.navigationController?.interactivePopGestureRecognizer.enabled = true } else { self.navigationItem.leftBarButtonItem = self.backButton // Handle the error } } }
Lebih lanjut tentang tampilan akan / memang muncul
Jika Anda tidak mendapatkan
viewWillAppear
viewDidDisappear
masalah, mari kita lihat contoh. Katakanlah Anda memiliki tiga pengontrol tampilan:Mari kita ikuti panggilan di
detailVC
saat Anda pergi darilistVC
kesettingsVC
dan kembali kelistVC
List> Detail (push detailVC)
Detail.viewDidAppear
<- munculDetail> Settings (push settingsVC)
Detail.viewDidDisappear
<- menghilangDan saat kita kembali ...
Settings> Detail (pop settingsVC)
Detail.viewDidAppear
<- munculDetail> List (pop detailVC)
Detail.viewDidDisappear
<- menghilangPerhatikan bahwa
viewDidDisappear
dipanggil beberapa kali, tidak hanya saat mundur, tetapi juga saat maju. Untuk operasi cepat yang mungkin diinginkan, tetapi untuk operasi yang lebih kompleks seperti menyimpan panggilan jaringan, mungkin tidak.sumber
didMoveToParantViewController:
harus melakukan pekerjaan saat tampilan tidak lagi terlihat. Bermanfaat untuk iOS7 dengan_ = self.navigationController?.popViewController(animated: true)
, jadi tidak hanya dipanggil saat menekan tombol Kembali. Saya mencari panggilan yang hanya berfungsi saat Kembali ditekan.Metode Pertama
- (void)didMoveToParentViewController:(UIViewController *)parent { if (![parent isEqual:self.parentViewController]) { NSLog(@"Back pressed"); } }
Metode Kedua
-(void) viewWillDisappear:(BOOL)animated { if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) { // back button was pressed. We know this is true because self is no longer // in the navigation stack. } [super viewWillDisappear:animated]; }
sumber
Mereka yang mengklaim bahwa ini tidak berhasil adalah salah:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if self.isMovingFromParent { print("we are being popped") } }
Itu bekerja dengan baik. Jadi apa yang menyebabkan mitos tersebar luas bahwa hal itu tidak terjadi?
Masalahnya tampaknya karena implementasi yang salah dari metode yang berbeda , yaitu implementasi
willMove(toParent:)
lupa meneleponsuper
.Jika Anda menerapkan
willMove(toParent:)
tanpa memanggilsuper
, makaself.isMovingFromParent
akanfalse
dan penggunaanviewWillDisappear
akan tampak gagal. Itu tidak gagal; Anda memecahkannya.CATATAN: Masalah sebenarnya biasanya adalah pengontrol tampilan kedua yang mendeteksi bahwa pengontrol tampilan pertama muncul. Silakan lihat juga pembahasan yang lebih umum di sini: Unified UIViewController "menjadi terdepan" deteksi?
EDIT Sebuah komentar menyarankan bahwa ini harus
viewDidDisappear
daripadaviewWillDisappear
.sumber
true
untuk gerakan pop gesek interaktif - dari tepi kiri pengontrol tampilan - bahkan jika gesekan tidak sepenuhnya meletuskannya. Jadi, alih-alih memeriksanyawillDisappear
, melakukannya di tempatdidDisappear
kerja.Saya telah bermain (atau berkelahi) dengan masalah ini selama dua hari. IMO pendekatan terbaik hanya dengan membuat kelas ekstensi dan protokol, seperti ini:
@protocol UINavigationControllerBackButtonDelegate <NSObject> /** * Indicates that the back button was pressed. * If this message is implemented the pop logic must be manually handled. */ - (void)backButtonPressed; @end @interface UINavigationController(BackButtonHandler) @end @implementation UINavigationController(BackButtonHandler) - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item { UIViewController *topViewController = self.topViewController; BOOL wasBackButtonClicked = topViewController.navigationItem == item; SEL backButtonPressedSel = @selector(backButtonPressed); if (wasBackButtonClicked && [topViewController respondsToSelector:backButtonPressedSel]) { [topViewController performSelector:backButtonPressedSel]; return NO; } else { [self popViewControllerAnimated:YES]; return YES; } } @end
Ini berfungsi karena
UINavigationController
akan menerima panggilan kenavigationBar:shouldPopItem:
setiap kali pengontrol tampilan muncul. Di sana kami mendeteksi apakah kembali ditekan atau tidak (tombol lain). Satu-satunya hal yang harus Anda lakukan adalah mengimplementasikan protokol dalam pengontrol tampilan di mana tombol kembali ditekan.Ingatlah untuk memasukkan pengontrol tampilan secara manual ke dalam
backButtonPressedSel
, jika semuanya baik-baik saja.Jika Anda sudah membuat subclass
UINavigationViewController
dan mengimplementasikannavigationBar:shouldPopItem:
jangan khawatir, ini tidak akan mengganggu.Anda mungkin juga tertarik untuk menonaktifkan gerakan punggung.
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }
sumber
Ini berfungsi untuk saya di iOS 9.3.x dengan Swift:
override func didMoveToParentViewController(parent: UIViewController?) { super.didMoveToParentViewController(parent) if parent == self.navigationController?.parentViewController { print("Back tapped") } }
Tidak seperti solusi lain di sini, ini sepertinya tidak memicu secara tak terduga.
sumber
Sebagai catatan, menurut saya ini lebih dari apa yang dia cari…
UIBarButtonItem *l_backButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:self action:@selector(backToRootView:)]; self.navigationItem.leftBarButtonItem = l_backButton; - (void) backToRootView:(id)sender { // Perform some custom code [self.navigationController popToRootViewControllerAnimated:YES]; }
sumber
Anda dapat menggunakan tombol panggil balik, seperti ini:
- (BOOL) navigationShouldPopOnBackButton { [self backAction]; return NO; } - (void) backAction { // your code goes here // show confirmation alert, for example // ... }
untuk versi cepat Anda dapat melakukan sesuatu seperti dalam lingkup global
extension UIViewController { @objc func navigationShouldPopOnBackButton() -> Bool { return true } } extension UINavigationController: UINavigationBarDelegate { public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool { return self.topViewController?.navigationShouldPopOnBackButton() ?? true } }
Di bawah yang Anda letakkan di viewcontroller tempat Anda ingin mengontrol aksi tombol kembali:
override func navigationShouldPopOnBackButton() -> Bool { self.backAction()//Your action you want to perform. return true }
sumber
navigationShouldPopOnBackButton
asalnya? Ini bukan bagian dari API publik.Seperti yang
purrrminator
dikatakan, jawaban olehelitalon
tidak sepenuhnya benar, karenayour stuff
akan dijalankan bahkan ketika memunculkan pengontrol secara terprogram.Solusi yang saya temukan sejauh ini tidak terlalu bagus, tetapi berhasil untuk saya. Selain apa yang
elitalon
dikatakan, saya juga memeriksa apakah saya muncul secara terprogram atau tidak:- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if ((self.isMovingFromParentViewController || self.isBeingDismissed) && !self.isPoppingProgrammatically) { // Do your stuff here } }
Anda harus menambahkan properti itu ke pengontrol Anda dan mengaturnya ke YES sebelum muncul secara terprogram:
self.isPoppingProgrammatically = YES; [self.navigationController popViewControllerAnimated:YES];
Terima kasih atas bantuan Anda!
sumber
Cara terbaik adalah dengan menggunakan metode delegasi UINavigationController
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
Dengan menggunakan ini, Anda dapat mengetahui pengontrol apa yang menampilkan UINavigationController.
if ([viewController isKindOfClass:[HomeController class]]) { NSLog(@"Show home controller"); }
sumber
Saya telah memecahkan masalah ini dengan menambahkan UIControl ke navigationBar di sisi kiri.
UIControl *leftBarItemControl = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, 90, 44)]; [leftBarItemControl addTarget:self action:@selector(onLeftItemClick:) forControlEvents:UIControlEventTouchUpInside]; self.leftItemControl = leftBarItemControl; [self.navigationController.navigationBar addSubview:leftBarItemControl]; [self.navigationController.navigationBar bringSubviewToFront:leftBarItemControl];
Dan Anda harus ingat untuk menghapusnya saat tampilan akan hilang:
- (void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.leftItemControl) { [self.leftItemControl removeFromSuperview]; } }
Itu saja!
sumber
Anda harus memeriksa Protokol UINavigationBarDelegate . Dalam hal ini Anda mungkin ingin menggunakan metode navigationBar: shouldPopItem:.
sumber
Seperti yang dikatakan Coli88, Anda harus memeriksa protokol UINavigationBarDelegate.
Dalam cara yang lebih umum, Anda juga dapat menggunakan
- (void)viewWillDisapear:(BOOL)animated
untuk melakukan pekerjaan kustom saat tampilan yang dipertahankan oleh pengontrol tampilan yang terlihat saat ini akan menghilang. Sayangnya, cover ini akan mengganggu push dan pop case.sumber
Untuk Swift dengan UINavigationController:
override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if self.navigationController?.topViewController != self { print("back button tapped") } }
sumber
Jawaban 7ynk3r sangat dekat dengan apa yang saya gunakan pada akhirnya tetapi perlu beberapa penyesuaian:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item { UIViewController *topViewController = self.topViewController; BOOL wasBackButtonClicked = topViewController.navigationItem == item; if (wasBackButtonClicked) { if ([topViewController respondsToSelector:@selector(navBackButtonPressed)]) { // if user did press back on the view controller where you handle the navBackButtonPressed [topViewController performSelector:@selector(navBackButtonPressed)]; return NO; } else { // if user did press back but you are not on the view controller that can handle the navBackButtonPressed [self popViewControllerAnimated:YES]; return YES; } } else { // when you call popViewController programmatically you do not want to pop it twice return YES; } }
sumber
self.navigationController.isMovingFromParentViewController tidak berfungsi lagi di iOS8 dan 9 saya menggunakan:
-(void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; if (self.navigationController.topViewController != self) { // Is Popping } }
sumber
(CEPAT)
solusi yang akhirnya ditemukan .. metode yang kami cari adalah "willShowViewController" yang merupakan metode delegasi dari UINavigationController
//IMPORT UINavigationControllerDelegate !! class PushedController: UIViewController, UINavigationControllerDelegate { override func viewDidLoad() { //set delegate to current class (self) navigationController?.delegate = self } func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) { //MyViewController shoud be the name of your parent Class if var myViewController = viewController as? MyViewController { //YOUR STUFF } } }
sumber
MyViewController
untukPushedController
.