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?

bobobobo
sumber
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.

Ben S
sumber
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.

Dulgan
sumber
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;
Jesse
sumber
8

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

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}
csch
sumber
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
Ozgur
sumber
Saya baru saja menambahkan bagian ini tanpa membaca jawaban Anda, Anda benar-benar benar dengan "firstObject" menjadi lebih baik dari [0]
dulgan
1

Bagaimana jika meminta UIApplication singleton untuk keyWindow - nya , dan dari UIWindow tersebut meminta pengontrol tampilan root ( properti rootViewController -nya ):

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Basil Bourque
sumber
bagaimana cara mendapatkan pengontrol navigasi?
Yestay Muratov
Ini adalah saran yang salah karena bagaimana jika aplikasi rootviewcontroller saya adalah UITabBarViewController?
Sandeep Rana
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)
        ...
    }
    
Maris
sumber
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.

Pedro Trujillo
sumber
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)
AechoLiu
sumber