Bagaimana cara mendapatkan RootViewController dari pengontrol yang didorong?


137

Jadi, saya mendorong pengontrol tampilan dari RootViewController seperti:

[self.navigationController pushViewController: anotherViewController animasi: YES];

TAPI, DARI anotherViewControllersekarang, saya ingin mengakses RootViewController lagi.

Saya mencoba

// (di dalam anotherViewController sekarang)
/// RootViewController * root = (RootViewController *) self.parentViewController; // Tidak.
// err
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // IYA!! berhasil

Saya tidak yakin MENGAPA ini berhasil dan saya tidak yakin apakah itu cara terbaik untuk melakukannya. Adakah yang bisa memberi komentar tentang cara yang lebih baik untuk mendapatkan RootViewController dari pengontrol yang Anda dorong ke navigationController RootViewController dan apakah cara yang saya lakukan dapat diandalkan atau tidak?


Apa yang telah Anda lakukan dapat diandalkan untuk mendapatkan pengontrol tampilan root (yang pertama dalam hierarki navigasi), jika Anda ingin mendapatkan akses ke pengontrol tampilan "belakang", lihat jawaban saya.
Ben S

Jawaban:


134

Gunakan properti viewControllers dari UINavigationController. Kode contoh:

// Inside another ViewController
NSArray *viewControllers = self.navigationController.viewControllers;
UIViewController *rootViewController = [viewControllers objectAtIndex:viewControllers.count - 2];

Ini adalah cara standar untuk mendapatkan pengontrol tampilan "belakang". Alasan objectAtIndex:0berfungsi adalah karena pengontrol tampilan yang Anda coba akses juga merupakan root, jika Anda lebih dalam di navigasi, tampilan belakang tidak akan sama dengan tampilan root.


6
:) ty. Sepertinya masih hacky - :) Saya benar-benar ingin anggota "resmi" melakukan pekerjaan itu, seperti self.navigationController.rootViewController, tapi sayangnya, tidak ada yang seperti itu ..
bobobobo

62
Kode di atas salah. rootViewControllerhilang *sebelumnya, dan indeks seharusnya 0. Kode dalam pertanyaan benar:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
ma11hew28

2
Setuju: kode di atas mengembalikan pengontrol tampilan induk , bukan pengontrol tampilan root seperti yang diminta OP. Tetap saja, itulah yang Ben S katakan akan dia lakukan, dia hanya tidak cukup menunjukkannya.
Ivan Vučica

Baris ke-2 harus membaca: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Billy

180

Versi Swift:

var rootViewController = self.navigationController?.viewControllers.first

Versi ObjectiveC:

UIViewController *rootViewController = [self.navigationController.viewControllers firstObject];

Di mana self adalah turunan dari UIViewController yang disematkan dalam UINavigationController.


Bisakah saya mendapatkan navigationController dari ViewController lain dari kelas yang berbeda?
sasquatch

Setiap subkelas UIViewController akan memiliki properti navigationController, yang mengarah ke kelas UINavigationController pertama yang cocok dengan parentViewController.
Dulgan

12

Versi yang sedikit lebih jelek dari hal yang sama disebutkan dalam hampir semua jawaban ini:

UIViewController *rootViewController = [[self.navigationController viewControllers] firstObject];

dalam kasus Anda, saya mungkin akan melakukan sesuatu seperti:

di dalam subkelas UINavigationController Anda :

- (UIViewController *)rootViewController
{
    return [[self viewControllers] firstObject];
}

maka Anda dapat menggunakan:

UIViewController *rootViewController = [self.navigationController rootViewController];

edit

OP meminta properti di komentar.

jika Anda mau, Anda dapat mengaksesnya melalui sesuatu seperti self.navigationController.rootViewControllerhanya dengan menambahkan properti readonly ke header Anda:

@property (nonatomic, readonly, weak) UIViewController *rootViewController;

8

Untuk semua yang tertarik dengan ekstensi cepat, inilah yang saya gunakan sekarang:

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}

1
Terima kasih! Anda juga dapat menghapus "sebagai? UIViewController"
atereshkov

3

Sebagai tambahan @ dulgan ini jawabannya, itu selalu merupakan pendekatan yang baik untuk menggunakan firstObjectlebih objectAtIndex:0, karena sementara yang pertama kembali nil jika tidak ada objek dalam array, satu yang terakhir melempar pengecualian.

UIViewController *rootViewController = self.navigationController.rootViewController;

Atau, akan menjadi nilai tambah yang besar bagi Anda untuk membuat kategori bernama UINavigationController+Additionsdan menentukan metode Anda di dalamnya .

@interface UINavigationController (Additions)

- (UIViewController *)rootViewController;

@end

@implementation UINavigationController (Additions)

- (UIViewController *)rootViewController
{
    return self.viewControllers.firstObject;
}

@end

Saya baru saja menambahkan bagian ini tanpa membaca jawaban Anda, Anda benar-benar benar dengan "firstObject" menjadi lebih baik dari [0]
dulgan


1

Di sini saya menemukan metode universal untuk menavigasi dari tempat mana pun ke root.

  1. Anda membuat file Kelas baru dengan kelas ini, sehingga dapat diakses dari mana saja dalam proyek Anda:

    import UIKit
    
    class SharedControllers
    {
        static func navigateToRoot(viewController: UIViewController)
        {
            var nc = viewController.navigationController
    
            // If this is a normal view with NavigationController, then we just pop to root.
            if nc != nil
            {
                nc?.popToRootViewControllerAnimated(true)
                return
            }
    
            // Most likely we are in Modal view, so we will need to search for a view with NavigationController.
            let vc = viewController.presentingViewController
    
            if nc == nil
            {
                nc = viewController.presentingViewController?.navigationController
            }
    
            if nc == nil
            {
                nc = viewController.parentViewController?.navigationController
            }
    
            if vc is UINavigationController && nc == nil
            {
                nc = vc as? UINavigationController
            }
    
            if nc != nil
            {
                viewController.dismissViewControllerAnimated(false, completion:
                    {
                        nc?.popToRootViewControllerAnimated(true)
                })
            }
        }
    }
    
  2. Penggunaan dari mana saja dalam proyek Anda:

    {
        ...
        SharedControllers.navigateToRoot(self)
        ...
    }
    

0

Ini berhasil untuk saya:

Ketika pengontrol tampilan root saya tertanam di pengontrol navigasi:

UINavigationController * navigationController = (UINavigationController *)[[[[UIApplication sharedApplication] windows] firstObject] rootViewController];
RootViewController * rootVC = (RootViewController *)[[navigationController viewControllers] firstObject];

Ingat itu keyWindowsudah usang.


0

Saya mengalami kondisi yang aneh.

self.viewControllers.first bukan root viewController selalu.

Secara umum, self.viewControllers.firstmemang root viewController. Tapi terkadang tidak.

class MyCustomMainNavigationController: UINavigationController {

    function configureForView(_ v: UIViewController, animated: Bool) {
        let root = self.viewControllers.first
        let isRoot = (v == root)
    
        // Update UI based on isRoot
        // ....
    }
}

extension MyCustomMainNavigationController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, 
        willShow viewController: UIViewController, 
        animated: Bool) {

        self.configureForView(viewController, animated: animated)
    }
}

Masalah saya:

Umumnya, self.viewControllers.firstadalah rootviewController. Tapi, saat saya menelepon popToRootViewController(animated:), lalu terpicu navigationController(_:willShow:animated:). Saat ini, self.viewControllers.firstBUKAN root viewController, ini adalah viewController terakhir yang akan menghilang.

Ringkasan

  • self.viewControllers.firsttidak selalu rootviewController. Terkadang, ini akan menjadi viewController terakhir.

Jadi, saya sarankan untuk tetap rootViewControllermenggunakan properti ketika self.viewControllersHANYA memiliki satu viewController. Saya mendapatkan root viewController di viewDidLoad()UINavigationController kustom.

class MyCustomMainNavigationController: UINavigationController {
    fileprivate var myRoot: UIViewController!
    override func viewDidLoad() {

        super.viewDidLoad()

        // My UINavigationController is defined in storyboard. 
        // So at this moment, 
        // I can get root viewController by `self.topViewController!`
        let v = self.topViewController!
        self.myRoot = v
    }

}

Lingkungan:

  • iPhone 7 dengan iOS 14.0.1
  • Xcode 12.0.1 (12A7300)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.