Pengaturan aksi untuk tombol kembali pada pengontrol navigasi

180

Saya mencoba menimpa tindakan default tombol kembali di pengontrol navigasi. Saya telah memberikan target tindakan pada tombol khusus. Yang aneh adalah ketika menetapkannya meskipun atribut tombol punggung itu tidak memperhatikan mereka dan itu hanya muncul tampilan saat ini dan kembali ke root:

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                                  initWithTitle: @"Servers" 
                                  style:UIBarButtonItemStylePlain 
                                  target:self 
                                  action:@selector(home)];
self.navigationItem.backBarButtonItem = backButton;

Segera setelah saya mengaturnya leftBarButtonItemdi navigationItematasnya akan memanggil tindakan saya, namun kemudian tombol tersebut terlihat seperti putaran biasa dan bukan panah kembali:

self.navigationItem.leftBarButtonItem = backButton;

Bagaimana saya bisa mendapatkannya untuk memanggil tindakan kustom saya sebelum kembali ke tampilan root? Apakah ada cara untuk menimpa aksi kembali default, atau apakah ada metode yang selalu dipanggil ketika meninggalkan tampilan ( viewDidUnloadtidak melakukan itu)?

Bayan
sumber
action: @selector (home)]; membutuhkan: setelah aksi pemilih: @selektor (home :)]; kalau tidak, itu tidak akan berhasil
PartySoft
7
@PartySoft Itu tidak benar kecuali metode ini dideklarasikan dengan titik dua. Ini benar-benar valid untuk memiliki pemilih tombol panggilan yang tidak mengambil parameter apa pun.
mbm29414
3
Mengapa Apple tidak menyediakan tombol dengan gaya berbentuk seperti tombol kembali? Tampak cukup jelas.
JohnK
Lihatlah solusi di utas ini
Jiri Volejnik

Jawaban:

363

Coba letakkan ini di pengontrol tampilan tempat Anda ingin mendeteksi pers:

-(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];
}
William Jockusch
sumber
1
ini adalah solusi yang licin, bersih, bagus dan sangat dipikirkan
boliva
6
+1 peretasan hebat, tetapi tidak menawarkan kontrol atas animasi pop
matm
3
Tidak berfungsi untuk saya jika saya mengirim pesan ke delegasi melalui tombol dan delegasi mengeluarkan controller - ini masih menyala.
SAHM
21
Masalah lain adalah Anda tidak dapat membedakan apakah pengguna menekan tombol kembali atau jika Anda secara terprogram memanggil [self.navigationController popViewControllerAnimated: YES]
Chase Roberts
10
Hanya sebuah FYI: Versi cepat:if (find(self.navigationController!.viewControllers as! [UIViewController],self)==nil)
hEADcRASH
177

Saya telah menerapkan ekstensi UIViewController-BackButtonHandler . Tidak perlu mensubclass apa pun, cukup masukkan ke proyek Anda dan timpa navigationShouldPopOnBackButtonmetode di UIViewControllerkelas:

-(BOOL) navigationShouldPopOnBackButton {
    if(needsShowConfirmation) {
        // Show confirmation alert
        // ...
        return NO; // Ignore 'Back' button this time
    }
    return YES; // Process 'Back' button click and Pop view controler
}

Unduh aplikasi sampel .

setiap hari
sumber
16
Ini adalah solusi terbersih yang pernah saya lihat, lebih baik dan lebih sederhana daripada menggunakan UIButton kustom Anda sendiri. Terima kasih!
ramirogm
4
Ini navigationBar:shouldPopItem:bukan metode pribadi karena ini merupakan bagian dari UINavigationBarDelegateprotokol.
onegray
1
tetapi apakah UINavigationController sudah mengimplementasikan delegate (untuk kembali YES)? atau akankah itu di masa depan? subklas mungkin merupakan opsi yang lebih aman
Sam
8
Saya baru saja mengimplementasikan ini (BTW cukup keren) di iOS 7.1 dan memperhatikan bahwa setelah NOkembali tombol kembali tetap dalam keadaan dinonaktifkan (secara visual, karena masih menerima dan bereaksi terhadap sentuhan peristiwa). Saya mengatasinya dengan menambahkan elsepernyataan ke shouldPopcek dan bersepeda melalui subviews bilah navigasi, dan menetapkan alphanilai kembali ke 1 jika diperlukan di dalam blok animasi: gist.github.com/idevsoftware/9754057
boliva
2
Ini adalah salah satu ekstensi terbaik yang pernah saya lihat. Terima kasih banyak.
Srikanth
42

Tidak seperti yang dikatakan Amagrammer, itu mungkin. Anda harus mensubklasifikasikan Anda navigationController. Saya menjelaskan semuanya di sini (termasuk kode contoh).

HansPinckaers
sumber
Dokumentasi Apple ( developer.apple.com/iphone/library/documentation/UIKit/… ) mengatakan bahwa "Kelas ini tidak dimaksudkan untuk subklasifikasi". Meskipun saya tidak yakin apa yang mereka maksud dengan ini - mereka bisa berarti "Anda biasanya tidak perlu melakukan itu", atau mereka bisa berarti "kami akan menolak aplikasi Anda jika Anda mengacaukan dengan controller kami" ...
Kuba Suder
Ini tentu saja satu-satunya cara untuk melakukannya. Seandainya saya bisa memberi Anda lebih banyak poin, Hans!
Adam Eberbach
1
Bisakah Anda benar-benar mencegah tampilan keluar menggunakan metode ini? Apa yang akan Anda buat kembali metode popViewControllerAnimated jika Anda ingin tampilan tidak keluar?
JosephH
1
Ya kamu bisa. Hanya saja, jangan panggil metode superclass dalam implementasi Anda, waspadalah! Anda seharusnya tidak melakukan itu, pengguna berharap untuk kembali dalam navigasi. Yang bisa Anda lakukan adalah meminta konfirmasi. Menurut dokumentasi Apples, popViewController mengembalikan: "Pengontrol tampilan yang muncul dari tumpukan." Jadi ketika tidak ada yang muncul Anda harus mengembalikan nol;
HansPinckaers
1
@HansPickaers Saya pikir jawaban Anda tentang mencegah tampilan keluar mungkin agak salah. Jika saya menampilkan pesan 'konfirmasi' dari implementasi subclass dari popViewControllerAnimated :, NavigationBar masih menjiwai satu tingkat di pohon terlepas dari apa yang saya kembalikan. Ini sepertinya karena mengklik tombol kembali memanggil shouldPopNavigationItem pada bilah navigasi. Saya mengembalikan nol dari metode subclass saya sebagai recommendd.
deepwinter
15

Versi Swift:

(dari https://stackoverflow.com/a/19132881/826435 )

Di controller tampilan Anda, Anda hanya menyesuaikan diri dengan protokol dan melakukan tindakan apa pun yang Anda butuhkan:

extension MyViewController: NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool {
        performSomeActionOnThePressOfABackButton()
        return false
    }
}

Kemudian buat kelas, katakan NavigationController+BackButton, dan cukup salin-tempel kode di bawah ini:

protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

extension UINavigationController {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // Prevents from a synchronization issue of popping too many navigation items
        // and not enough view controllers or viceversa from unusual tapping
        if viewControllers.count < navigationBar.items!.count {
            return true
        }

        // Check if we have a view controller that wants to respond to being popped
        var shouldPop = true
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            shouldPop = viewController.shouldPopOnBackButtonPress()
        }

        if (shouldPop) {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            // Prevent the back button from staying in an disabled state
            for view in navigationBar.subviews {
                if view.alpha < 1.0 {
                    UIView.animate(withDuration: 0.25, animations: {
                        view.alpha = 1.0
                    })
                }
            }

        }

        return false
    }
}
kgaidis
sumber
Mungkin saya melewatkan sesuatu, tetapi itu tidak berhasil untuk saya, metode performSomeActionOnThePressOfABackTombol ekstensi tidak pernah disebut
Turvy
@FlorentBreton mungkin kesalahpahaman? shouldPopOnBackButtonPressharus dipanggil selama tidak ada bug. performSomeActionOnThePressOfABackButtonhanyalah metode buatan yang tidak ada.
kgaidis
Saya memahaminya, itu sebabnya saya membuat metode performSomeActionOnThePressOfABackButtondi controller saya untuk melakukan tindakan tertentu ketika tombol kembali ditekan, tetapi metode ini tidak pernah dipanggil, tindakan itu adalah pengembalian kembali yang normal
Turvy
1
Tidak bekerja untuk saya juga. Metode shouldPop tidak pernah dipanggil. Apakah Anda menetapkan delegasi di suatu tempat?
Tim Autin
@TimAutin Saya baru saja menguji ini lagi dan sepertinya ada sesuatu yang berubah. Bagian penting untuk memahami bahwa dalam UINavigationController, navigationBar.delegatediatur ke pengontrol navigasi. Jadi metode HARUS dipanggil. Namun, di Swift, saya tidak bisa membuat mereka dipanggil, bahkan di subkelas. Namun, saya membuatnya dipanggil dalam Objective-C, jadi saya hanya akan menggunakan versi Objective-C untuk saat ini. Bisa jadi bug Swift.
kgaidis
5

Itu tidak mungkin dilakukan secara langsung. Ada beberapa alternatif:

  1. Buat kustom Anda sendiri UIBarButtonItemyang divalidasi pada ketuk dan muncul jika tes lulus
  2. Validasi konten bidang formulir menggunakan UITextFieldmetode delegasi, seperti -textFieldShouldReturn:, yang dipanggil setelah tombol Returnatau Doneditekan pada keyboard

Kelemahan dari opsi pertama adalah bahwa gaya panah menunjuk-kiri tombol kembali tidak dapat diakses dari tombol bilah kustom. Jadi, Anda harus menggunakan gambar atau pergi dengan tombol gaya biasa.

Opsi kedua bagus karena Anda mendapatkan bidang teks kembali dalam metode delegasi, sehingga Anda dapat menargetkan logika validasi Anda ke bidang teks tertentu yang dikirim ke metode panggilan balik delegasi.

Alex Reynolds
sumber
5

Untuk beberapa alasan threading, solusi yang disebutkan oleh @HansPinckaers tidak tepat untuk saya, tapi saya menemukan cara yang sangat mudah untuk menangkap sentuhan pada tombol kembali, dan saya ingin menjepit ini di sini kalau-kalau ini bisa menghindari jam penipuan untuk orang lain. Caranya sangat mudah: cukup tambahkan UIButton transparan sebagai subview ke UINavigationBar Anda, dan atur penyeleksi Anda untuknya seolah-olah itu adalah tombol asli! Berikut ini contoh menggunakan Monotouch dan C #, tetapi terjemahan ke objektif-c seharusnya tidak terlalu sulit untuk ditemukan.

public class Test : UIViewController {
    public override void ViewDidLoad() {
        UIButton b = new UIButton(new RectangleF(0, 0, 60, 44)); //width must be adapted to label contained in button
        b.BackgroundColor = UIColor.Clear; //making the background invisible
        b.Title = string.Empty; // and no need to write anything
        b.TouchDown += delegate {
            Console.WriteLine("caught!");
            if (true) // check what you want here
                NavigationController.PopViewControllerAnimated(true); // and then we pop if we want
        };
        NavigationController.NavigationBar.AddSubview(button); // insert the button to the nav bar
    }
}

Fakta menyenangkan: untuk tujuan pengujian dan untuk menemukan dimensi yang baik untuk tombol palsu saya, saya mengatur warna latar belakangnya menjadi biru ... Dan itu ditampilkan di belakang tombol kembali! Bagaimanapun, masih menangkap sentuhan yang menargetkan tombol asli.

psiko
sumber
3

Teknik ini memungkinkan Anda untuk mengubah teks tombol "kembali" tanpa mempengaruhi judul pengontrol tampilan atau melihat perubahan teks tombol kembali selama animasi.

Tambahkan ini ke metode init di controller tampilan panggilan :

UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];   
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];
Jason Moore
sumber
3

Cara termudah

Anda dapat menggunakan metode delegasi UINavigationController. Metode willShowViewControllerini dipanggil ketika tombol kembali VC Anda ditekan. Lakukan apa pun yang Anda inginkan ketika kembali dengan ditekan

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
Zar E Ahmer
sumber
Pastikan pengontrol tampilan Anda menetapkan dirinya sebagai delegasi dari navigationController yang diwarisi dan sesuai dengan protokol UINavigationControllerDelegate
Justin Milo
3

Inilah solusi Swift saya. Di subkelas UIViewController Anda, ganti metode navigasiShouldPopOnBackButton.

extension UIViewController {
    func navigationShouldPopOnBackButton() -> Bool {
        return true
    }
}

extension UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {
        if let vc = self.topViewController {
            if vc.navigationShouldPopOnBackButton() {
                self.popViewControllerAnimated(true)
            } else {
                for it in navigationBar.subviews {
                    let view = it as! UIView
                    if view.alpha < 1.0 {
                        [UIView .animateWithDuration(0.25, animations: { () -> Void in
                            view.alpha = 1.0
                        })]
                    }
                }
                return false
            }
        }
        return true
    }

}
AutomatonTec
sumber
Navigasi metode overrideShouldPopOnBackButton di UIViewController tidak berfungsi - Program mengeksekusi metode induk bukan yang overriden. Ada solusi untuk itu? Adakah yang memiliki masalah yang sama?
Pawel Cala
semua jalan kembali ke rootview jika kembali benar
Pawriwes
@ Pamriwes Inilah solusi yang saya tulis yang sepertinya berfungsi untuk saya: stackoverflow.com/a/34343418/826435
kgaidis
3

Menemukan solusi yang mempertahankan gaya tombol kembali juga. Tambahkan metode berikut ke pengontrol tampilan Anda.

-(void) overrideBack{

    UIButton *transparentButton = [[UIButton alloc] init];
    [transparentButton setFrame:CGRectMake(0,0, 50, 40)];
    [transparentButton setBackgroundColor:[UIColor clearColor]];
    [transparentButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.navigationController.navigationBar addSubview:transparentButton];


}

Sekarang berikan fungsionalitas sesuai kebutuhan dalam metode berikut:

-(void)backAction:(UIBarButtonItem *)sender {
    //Your functionality
}

Yang dilakukannya hanyalah menutup tombol kembali dengan tombol transparan;)

Sarasranglt
sumber
3

Overriding navigationBar (_ navigationBar: shouldPop) : Ini bukan ide yang baik, bahkan jika itu berfungsi. bagi saya itu menghasilkan crash acak saat menavigasi kembali. Saya menyarankan Anda untuk hanya mengganti tombol kembali dengan menghapus tombol BackButton default dari navigationItem dan membuat tombol kembali kustom seperti di bawah ini:

override func viewDidLoad(){
   super.viewDidLoad()
   
   navigationItem.leftBarButton = .init(title: "Go Back", ... , action: #selector(myCutsomBackAction) 

   ...
 
}

======================================

Membangun tanggapan sebelumnya dengan UIAlert di Swift5 dengan cara Asinkron


protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ())
}

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
      
        if viewControllers.count < navigationBar.items!.count {
            return true
        }
        
        // Check if we have a view controller that wants to respond to being popped
        
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            
            viewController.shouldPopOnBackButtonPress { shouldPop in
                if (shouldPop) {
                    /// on confirm => pop
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                } else {
                    /// on cancel => do nothing
                }
            }
            /// return false => so navigator will cancel the popBack
            /// until user confirm or cancel
            return false
        }else{
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        }
        return true
    }
}

Di controller Anda


extension MyController: NavigationControllerBackButtonDelegate {
    
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ()) {
    
        let msg = "message"
        
        /// show UIAlert
        alertAttention(msg: msg, actions: [
            
            .init(title: "Continuer", style: .destructive, handler: { _ in
                completion(true)
            }),
            .init(title: "Annuler", style: .cancel, handler: { _ in
                completion(false)
            })
            ])
   
    }

}
brahim
sumber
Bisakah Anda memberikan detail tentang apa yang terjadi dengan if viewControllers.count <navigationBar.items! .Count {return true} periksa?
H4Hugo
// Mencegah masalah sinkronisasi dari memunculkan terlalu banyak item navigasi // dan tidak cukup pengontrol tampilan atau sebaliknya dari penyadapan yang tidak biasa
brahimm
2

Saya tidak percaya ini mungkin, dengan mudah. Satu-satunya cara saya percaya untuk mengatasi ini adalah dengan membuat gambar panah tombol kembali Anda sendiri untuk ditempatkan di sana. Awalnya itu membuat saya frustasi, tetapi saya mengerti mengapa, demi konsistensi, itu diabaikan.

Anda dapat mendekati (tanpa panah) dengan membuat tombol biasa dan menyembunyikan tombol kembali default:

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Servers" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;
Meltemi
sumber
2
Ya masalahnya adalah saya ingin itu terlihat seperti tombol kembali normal, hanya perlu untuk memanggil tindakan kustom saya terlebih dahulu ...
Parrots
2

Ada cara yang lebih mudah dengan hanya subclassing metode delegasi dari UINavigationBardan menimpa para ShouldPopItemmetode .

jazzyjef2002
sumber
Saya pikir Anda bermaksud mengatakan subkelas kelas UINavigationController dan menerapkan metode shouldPopItem. Itu bekerja dengan baik untuk saya. Namun, metode itu seharusnya tidak hanya mengembalikan YA atau TIDAK seperti yang Anda harapkan. Penjelasan dan solusi tersedia di sini: stackoverflow.com/a/7453933/462162
arlomedia
2

solusi onegray tidak aman. Menurut dokumen resmi oleh Apple, https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html , kita harus menghindari itu.

"Jika nama metode yang dideklarasikan dalam kategori adalah sama dengan metode di kelas asli, atau metode dalam kategori lain di kelas yang sama (atau bahkan superclass), perilaku tidak ditentukan untuk metode implementasi yang digunakan saat runtime. Ini cenderung menjadi masalah jika Anda menggunakan kategori dengan kelas Anda sendiri, tetapi dapat menyebabkan masalah saat menggunakan kategori untuk menambahkan metode ke kelas standar Cocoa atau Cocoa Touch. "

pengguna2612791
sumber
2

Menggunakan Swift:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    if self.navigationController?.topViewController != self {
        print("back button tapped")
    }
}
Murray Sagal
sumber
Pada iOS 10, dan mungkin sebelumnya, ini tidak lagi berfungsi.
Murray Sagal
2

Berikut ini adalah versi Swift 3 dari jawaban @oneway untuk menangkap acara tombol navigasi bar sebelum dipecat. Karena UINavigationBarDelegatetidak dapat digunakan untuk UIViewController, Anda perlu membuat delegasi yang akan dipicu ketika navigationBar shouldPopdipanggil.

@objc public protocol BackButtonDelegate {
      @objc optional func navigationShouldPopOnBackButton() -> Bool 
}

extension UINavigationController: UINavigationBarDelegate  {

    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

        if viewControllers.count < (navigationBar.items?.count)! {                
            return true
        }

        var shouldPop = true
        let vc = self.topViewController

        if vc.responds(to: #selector(vc.navigationShouldPopOnBackButton)) {
            shouldPop = vc.navigationShouldPopOnBackButton()
        }

        if shouldPop {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            for subView in navigationBar.subviews {
                if(0 < subView.alpha && subView.alpha < 1) {
                    UIView.animate(withDuration: 0.25, animations: {
                        subView.alpha = 1
                    })
                }
            }
        }

        return false
    }
}

Dan kemudian, di controller tampilan Anda tambahkan fungsi delegasi:

class BaseVC: UIViewController, BackButtonDelegate {
    func navigationShouldPopOnBackButton() -> Bool {
        if ... {
            return true
        } else {
            return false
        }        
    }
}

Saya menyadari bahwa kami sering ingin menambahkan pengontrol lansiran bagi pengguna untuk memutuskan apakah mereka ingin kembali. Jika demikian, Anda selalu dapat return falsedi navigationShouldPopOnBackButton()fungsi dan menutup controller pandangan Anda dengan melakukan sesuatu seperti ini:

func navigationShouldPopOnBackButton() -> Bool {
     let alert = UIAlertController(title: "Warning",
                                          message: "Do you want to quit?",
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { UIAlertAction in self.yes()}))
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { UIAlertAction in self.no()}))
            present(alert, animated: true, completion: nil)
      return false
}

func yes() {
     print("yes")
     DispatchQueue.main.async {
            _ = self.navigationController?.popViewController(animated: true)
        }
}

func no() {
    print("no")       
}
Daftar Hukum
sumber
Saya mendapatkan kesalahan: Value of type 'UIViewController' has no member 'navigationShouldPopOnBackButton' ketika saya mencoba untuk mengkompilasi kode Anda, untuk baris if vc.responds(to: #selector(v...Juga, self.topViewControllermengembalikan opsional dan ada peringatan untuk itu juga.
Sankar
FWIW, saya telah memperbaiki kode itu dengan membuat: let vc = self.topViewController as! MyViewControllerdan sepertinya berfungsi dengan baik sejauh ini. Jika Anda yakin itu adalah perubahan yang tepat, Anda dapat mengedit kode. Juga, jika Anda merasa bahwa itu tidak boleh dilakukan, saya akan senang mengetahui mengapa. Terima kasih untuk kode ini. Anda mungkin harus menulis posting blog tentang ini, karena jawaban ini dikubur sesuai suara.
Sankar
@SankarP Alasan Anda mendapatkan kesalahan itu adalah Anda MyViewControllermungkin tidak setuju BackButtonDelegate. Daripada memaksa membuka, Anda harus lakukan guard let vc = self.topViewController as? MyViewController else { return true }untuk menghindari kemungkinan kecelakaan.
Lawliet
Terima kasih. Saya pikir pernyataan penjaga harus menjadi: guard let vc = self.topViewController as? MyViewController else { self.popViewController(animated: true) return true }untuk memastikan bahwa layar bergerak ke halaman yang tepat jika itu tidak dapat dilemparkan dengan benar. Saya mengerti sekarang bahwa navigationBarfungsi dipanggil di semua VC dan bukan hanya viewcontroller di mana kode ini ada. Mungkin akan baik untuk memperbarui kode dalam jawaban Anda juga? Terima kasih.
Sankar
2

Versi Swift 4 iOS 11.3:

Ini didasarkan pada jawaban dari kgaidis dari https://stackoverflow.com/a/34343418/4316579

Saya tidak yakin kapan ekstensi berhenti berfungsi, tetapi pada saat penulisan ini (Swift 4), tampaknya ekstensi tidak akan lagi dijalankan kecuali Anda menyatakan kesesuaian UINavigationBarDelegate seperti dijelaskan di bawah ini.

Semoga ini bisa membantu orang yang bertanya-tanya mengapa ekstensi mereka tidak lagi berfungsi.

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

    }
}
Edward L.
sumber
1

Dengan menggunakan variabel target dan tindakan yang saat ini Anda tinggalkan 'nil', Anda harus dapat menghubungkan dialog penyimpanan Anda sehingga mereka dipanggil ketika tombol "dipilih". Awas, ini bisa dipicu pada saat-saat aneh.

Saya sebagian besar setuju dengan Amagrammer, tetapi saya tidak berpikir akan sulit untuk membuat tombol dengan panah kustom. Saya hanya akan mengganti nama tombol kembali, mengambil screenshot, photoshop ukuran tombol yang diperlukan, dan memilikinya menjadi gambar di bagian atas tombol Anda.

TahoeWolverine
sumber
Saya setuju Anda dapat photoshop dan saya pikir saya dapat melakukan ini jika saya benar-benar menginginkannya tetapi sekarang telah memutuskan untuk mengubah tampilan dan merasakan sedikit hal kecil agar ini berfungsi seperti yang saya inginkan.
John Ballinger
Ya, kecuali bahwa tindakan tidak dipicu ketika mereka dilampirkan ke backBarButtonItem. Saya tidak tahu apakah ini bug atau fitur; mungkin bahkan Apple tidak tahu. Mengenai latihan photoshopping, sekali lagi, saya akan khawatir bahwa Apple akan menolak aplikasi karena menyalahgunakan simbol kanonik.
Amagrammer
Heads-up: jawaban ini telah digabungkan dari duplikat.
Shog9
1

Anda dapat mencoba mengakses item Tombol Kanan Navigasi dan mengatur properti pemilihnya ... inilah referensi UIBarButtonItem referensi , hal lain jika pekerjaan doenst ini yang akan def bekerja adalah, mengatur item tombol kanan nav bar ke UIBarButtonItem kustom yang Anda inginkan buat dan atur pemilihnya ... semoga ini membantu

Daniel
sumber
Heads-up: jawaban ini telah digabungkan dari duplikat.
Shog9
1

Untuk formulir yang memerlukan input pengguna seperti ini, saya akan merekomendasikan memohonnya sebagai "modal" alih-alih bagian dari tumpukan navigasi Anda. Dengan begitu mereka harus mengurus bisnis pada formulir, maka Anda dapat memvalidasinya dan mengabaikannya menggunakan tombol kustom. Anda bahkan dapat mendesain bilah navigasi yang terlihat sama dengan sisa aplikasi Anda, tetapi memberi Anda lebih banyak kontrol.

Travis M.
sumber
Heads-up: jawaban ini telah digabungkan dari duplikat.
Shog9
1

Untuk mencegat tombol Kembali, cukup tutup dengan UIControl transparan dan sentuh sentuhannya.

@interface MyViewController : UIViewController
{
    UIControl   *backCover;
    BOOL        inhibitBackButtonBOOL;
}
@end

@implementation MyViewController
-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Cover the back button (cannot do this in viewWillAppear -- too soon)
    if ( backCover == nil ) {
        backCover = [[UIControl alloc] initWithFrame:CGRectMake( 0, 0, 80, 44)];
#if TARGET_IPHONE_SIMULATOR
        // show the cover for testing
        backCover.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.15];
#endif
        [backCover addTarget:self action:@selector(backCoverAction) forControlEvents:UIControlEventTouchDown];
        UINavigationBar *navBar = self.navigationController.navigationBar;
        [navBar addSubview:backCover];
    }
}

-(void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [backCover removeFromSuperview];
    backCover = nil;
}

- (void)backCoverAction
{
    if ( inhibitBackButtonBOOL ) {
        NSLog(@"Back button aborted");
        // notify the user why...
    } else {
        [self.navigationController popViewControllerAnimated:YES]; // "Back"
    }
}
@end
Jeff
sumber
Heads-up: jawaban ini telah digabungkan dari duplikat.
Shog9
1

Setidaknya di Xcode 5, ada solusi yang sederhana dan cukup bagus (tidak sempurna). Di IB, seret Item Tombol Bilah dari panel Utilities dan letakkan di sisi kiri Bilah Navigasi tempat tombol Kembali berada. Atur label menjadi "Kembali." Anda akan memiliki tombol yang berfungsi yang dapat Anda ikat ke IBAction Anda dan tutup viewController Anda. Saya melakukan beberapa pekerjaan dan kemudian memicu segue bersantai dan bekerja dengan sempurna.

Apa yang tidak ideal adalah tombol ini tidak mendapatkan <panah dan tidak meneruskan judul VC sebelumnya, tapi saya pikir ini bisa dikelola. Untuk tujuan saya, saya mengatur tombol Kembali yang baru menjadi tombol "Selesai" sehingga tujuannya jelas.

Anda juga berakhir dengan dua tombol Kembali di navigator IB, tetapi cukup mudah untuk memberi label kejelasan.

masukkan deskripsi gambar di sini

Dan Loughney
sumber
1

Cepat

override func viewWillDisappear(animated: Bool) {
    let viewControllers = self.navigationController?.viewControllers!
    if indexOfArray(viewControllers!, searchObject: self) == nil {
        // do something
    }
    super.viewWillDisappear(animated)
}

func indexOfArray(array:[AnyObject], searchObject: AnyObject)-> Int? {
    for (index, value) in enumerate(array) {
        if value as UIViewController == searchObject as UIViewController {
            return index
        }
    }
    return nil
}
zono
sumber
1

Pendekatan ini berhasil bagi saya (tetapi tombol "Kembali" tidak akan memiliki tanda "<"):

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem* backNavButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
                                                                      style:UIBarButtonItemStyleBordered
                                                                     target:self
                                                                     action:@selector(backButtonClicked)];
    self.navigationItem.leftBarButtonItem = backNavButton;
}

-(void)backButtonClicked
{
    // Do something...
    AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    [delegate.navController popViewControllerAnimated:YES];
}
Ivan
sumber
1

Versi cepat jawaban onegray

protocol RequestsNavigationPopVerification {
    var confirmationTitle: String { get }
    var confirmationMessage: String { get }
}

extension RequestsNavigationPopVerification where Self: UIViewController {
    var confirmationTitle: String {
        return "Go back?"
    }

    var confirmationMessage: String {
        return "Are you sure?"
    }
}

final class NavigationController: UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {

        guard let requestsPopConfirm = topViewController as? RequestsNavigationPopVerification else {
            popViewControllerAnimated(true)
            return true
        }

        let alertController = UIAlertController(title: requestsPopConfirm.confirmationTitle, message: requestsPopConfirm.confirmationMessage, preferredStyle: .Alert)

        alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                let dimmed = navigationBar.subviews.flatMap { $0.alpha < 1 ? $0 : nil }
                UIView.animateWithDuration(0.25) {
                    dimmed.forEach { $0.alpha = 1 }
                }
            })
            return
        })

        alertController.addAction(UIAlertAction(title: "Go back", style: .Default) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                self.popViewControllerAnimated(true)
            })
        })

        presentViewController(alertController, animated: true, completion: nil)

        return false
    }
}

Sekarang di setiap pengontrol, cukup ikuti RequestsNavigationPopVerificationdan perilaku ini diadopsi secara default.

Adam Waite
sumber
1

Menggunakan isMovingFromParentViewController

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)

    if self.isMovingFromParentViewController {
        // current viewController is removed from parent
        // do some work
    }
}
herrk
sumber
Bisakah Anda jelaskan lebih lanjut bagaimana ini membuktikan tombol kembali disadap?
Murray Sagal
Ini sederhana, tetapi hanya berfungsi jika Anda yakin untuk kembali ke tampilan itu dari tampilan anak mana pun yang Anda muat. Jika anak melewatkan tampilan ini saat kembali ke induk, kode Anda tidak akan dipanggil (tampilan sudah hilang tanpa dipindahkan dari induknya). Tapi itu masalah yang sama dengan hanya menangani peristiwa pada pemicu tombol Kembali seperti yang diminta oleh OP. Jadi ini adalah jawaban sederhana untuk pertanyaannya.
CMont
Ini sangat sederhana dan elegan. Aku menyukainya. Hanya satu masalah: ini juga akan menyala jika pengguna menggesek untuk kembali, bahkan jika mereka membatalkan di tengah jalan. Mungkin solusi yang lebih baik adalah dengan memasukkan kode ini viewDidDisappear. Dengan begitu itu hanya akan menyala setelah tampilan benar-benar hilang.
Phontaine Judd
1

Namun, jawaban dari @William benar, jika pengguna memulai gerakan swipe-to-go-back viewWillDisappearmetode ini dipanggil dan bahkan selftidak akan ada di tumpukan navigasi (yaitu, self.navigationController.viewControllerstidak akan berisi self), bahkan jika gesek tidak selesai dan pengontrol tampilan tidak benar-benar muncul. Dengan demikian, solusinya adalah:

  1. Nonaktifkan isyarat gesek untuk kembali viewDidAppeardan hanya mengizinkan menggunakan tombol kembali, dengan menggunakan:

    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    }
  2. Atau gunakan viewDidDisappearsaja, sebagai berikut:

    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        if (![self.navigationController.viewControllers containsObject:self])
        {
            // back button was pressed or the the swipe-to-go-back gesture was
            // completed. We know this is true because self is no longer
            // in the navigation stack.
        }
    }
boherna
sumber
0

Solusi yang saya temukan sejauh ini tidak terlalu baik, tetapi itu bekerja untuk saya. Mengambil jawaban ini , 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 controller Anda dan mengaturnya ke YA sebelum muncul secara terprogram:

self.isPoppingProgrammatically = YES;
[self.navigationController popViewControllerAnimated:YES];
Ferran Maylinch
sumber
0

Menemukan cara baru untuk melakukannya:

Objektif-C

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}

Cepat

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        println("Back Pressed")
    }
}
Ashish Kakkad
sumber