Cara menemukan pengontrol tampilan teratas di iOS

253

Saya telah mengalami beberapa kasus sekarang di mana akan lebih mudah untuk dapat menemukan controller tampilan "paling atas" (yang bertanggung jawab atas tampilan saat ini), tetapi belum menemukan cara untuk melakukannya.

Pada dasarnya tantangannya adalah ini: Mengingat bahwa seseorang mengeksekusi di kelas yang bukan pengontrol tampilan (atau tampilan) [dan tidak memiliki alamat tampilan aktif] dan belum melewati alamat pengontrol tampilan teratas ( atau, katakanlah, alamat pengontrol navigasi), apakah mungkin untuk menemukan pengontrol tampilan itu? (Dan, jika ya, bagaimana?)

Atau, jika gagal, apakah mungkin untuk menemukan tampilan teratas?

Hot Licks
sumber
Jadi maksudmu itu tidak mungkin.
Hot Licks
@Aniel tidak, saya mengatakan bahwa sepertinya kode Anda dapat menggunakan beberapa perancangan ulang, karena Anda seharusnya jarang perlu mengetahui hal ini. Juga, ide "paling atas" hanya valid dalam konteks tertentu, dan itupun tidak selalu.
Dave DeLong
@ Danielel saya salah membaca pertanyaan Anda. Ada banyak jika dan tetapi mencoba untuk menjawab yang satu ini. Itu tergantung pada aliran pengontrol tampilan Anda. @ Wilbur menjawab harus menjadi titik awal yang baik untuk melacaknya.
Deepak Danduprolu
Baiklah, mari sederhanakan ke kasus tertentu. Jika saya ingin menulis klon dari UIAlertView, bagaimana saya melakukannya? Perhatikan bahwa ini dapat berfungsi dengan baik tanpa memberikan aksesibilitas ke pengontrol atau tampilan lain.
Hot Licks
4
@Aniel: Menambahkan UIWindow kedua berfungsi dengan baik untuk overlay tampilan mirip peringatan.
Wilbur Vandrsmith

Jawaban:

75

iOS 4 memperkenalkan properti rootViewController di UIWindow:

[UIApplication sharedApplication].keyWindow.rootViewController;

Anda harus mengaturnya sendiri setelah membuat pengendali tampilan.

Wilbur Vandrsmith
sumber
155
Wilbur, ini akan memberimu kebalikan dari yang diminta op. rootViewController adalah pengendali tampilan dasar dan bukan yang paling atas.
m4rkk
3
m4rkk: "Paling atas" tergantung dari arah mana Anda melihat. Apakah pengendali baru ditambahkan ke atas (seperti tumpukan) atau bawah (seperti pohon)? Dalam kasus apa pun, OP menyebutkan pengontrol navigasi berada di atas, yang menyiratkan tampilan tumbuh turun.
Wilbur Vandrsmith
50
Kata "atas" digunakan untuk pengontrol tampilan, yaitu visualy di atas (seperti -[UINavigationController topViewController]). Lalu ada kata "root", yang merupakan akar dari pohon (seperti -[UIWindow rootViewController].
Tricertops
13
@ImpurestClub Saya tidak dapat menemukan di dalam dokumentasi , sepertinya Xcode tidak menemukannya.
Drux
4
@adib tidak, itu milik UINavigationController
David H
428

Saya pikir Anda memerlukan kombinasi dari jawaban yang diterima dan @ fishstix

+ (UIViewController*) topMostController
{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    return topController;
}

Swift 3.0+

func topMostController() -> UIViewController? {
    guard let window = UIApplication.shared.keyWindow, let rootViewController = window.rootViewController else {
        return nil
    }

    var topController = rootViewController

    while let newTopController = topController.presentedViewController {
        topController = newTopController
    }

    return topController
}
Eric
sumber
4
Selain itu, Anda dapat memeriksa UINavigationControllerdan meminta topViewControlleratau bahkan memeriksa UITabBarControllerdan meminta selectedViewController. Ini akan membuat Anda pengontrol tampilan yang saat ini dapat dilihat oleh pengguna.
Tricertops
33
Ini adalah solusi yang tidak lengkap, karena ini hanya melintasi hierarki pengendali tampilan yang disajikan secara modern, bukan hierarki childViewControllers (seperti yang digunakan oleh UINavigationController, UITabBarController, dll.).
algal
3
Ini adalah cara yang bagus untuk mengabstraksi penyajian pengontrol tampilan modal yang dilanjutkan ke status aplikasi saat ini, dalam kasus saya ini adalah layar masuk kembali kata sandi setelah aplikasi habis. Terima kasih!
erversteeg
11
@algal: tidak benar-benar: UITabBarController, UINavigationController yang sudah pengendali tampilan paling atas dalam hirarki. Tergantung pada apa yang ingin Anda lakukan dengan "pengontrol teratas" Anda mungkin tidak ingin melintasi mereka sama sekali dan mengutak-atik konten mereka. Dalam kasus saya itu adalah untuk menyajikan pengontrol modal di atas segalanya, dan untuk itu saya perlu mendapatkan UINaviationController atau UITabBarController, bukan konten mereka !!
Rick77
1
@ Rick77, jika ini benar, satu komentar kecil Anda yang terkubur di sini membuat tidak perlu banyak modifikasi rumit dalam jawaban lainnya. Karena tidak ada orang yang menyebutkan ini sama sekali, saya merasa saya harus meminta Anda untuk menegaskan bahwa itu benar. Dan jika ya, maka sangat penting bahwa itu layak untuk menjadi jawaban sendiri. Karena sebagian besar jawaban lain melakukan backflips yang mencoba mengatasi masalah ini. Anda akan menyelamatkan hidup!
Le Mot Juiced
150

Untuk menyelesaikan jawaban JonasG (yang meninggalkan pengontrol tab bar saat melintasi), inilah versi saya mengembalikan pengontrol tampilan yang saat ini terlihat:

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}
kleo
sumber
2
Bagus, ya saya lupa tentang pengontrol TabBar: P
JonasG
9
Tidak termasukchildViewControllers
Keren-
Lihatlah jawaban saya di bawah ini yang meningkatkan jawaban di atas dengan menangani case @kleo yang ditinggalkan seperti popovers, view controller ditambahkan sebagai subviews ke beberapa controller view lainnya saat melintasi
Rajesh
Jika Anda menggunakan return [self topViewControllerWithRootViewController: navigationController.visibleViewController] ;, visibleViewController sendiri mengembalikan pengontrol tampilan yang disajikan (JIKA ADA), bahkan jika itu adalah UIAlertController. Untuk seseorang yang perlu menghindari pengontrol lansiran ui, gunakan topViewController alih-alih visibleViewController
Johnykutty
Hanya untuk menambahkan 50 sen saya ke ini - saya berjuang untuk mendapatkan ini bekerja di viewcontroller saya yang memuat webView .. alasan mengapa saya tidak bisa mendapatkan ini bekerja adalah karena pandangan itu masih belum siap (tidak selesai memuat) dan karena itu tidak terlihat. Itu mengarah pada situasi di mana mendapatkan topViewContoller gagal, karena UINavigationController sedang mencoba untuk mendapatkan ViewController yang terlihat sementara belum ada ViewController yang terlihat. Jadi, jika ada yang menghadapi masalah ini, pastikan tampilan Anda selesai memuat sebelum Anda melakukan panggilan ke metode topViewController di atas.
mbuster
52

Versi non-rekursif lengkap, menangani berbagai skenario:

  • Pengontrol tampilan menyajikan tampilan lain
  • Pengontrol tampilan adalah a UINavigationController
  • Pengontrol tampilan adalah a UITabBarController

Objektif-C

 UIViewController *topViewController = self.window.rootViewController;
 while (true)
 {
     if (topViewController.presentedViewController) {
         topViewController = topViewController.presentedViewController;
     } else if ([topViewController isKindOfClass:[UINavigationController class]]) {
         UINavigationController *nav = (UINavigationController *)topViewController;
         topViewController = nav.topViewController;
     } else if ([topViewController isKindOfClass:[UITabBarController class]]) {
         UITabBarController *tab = (UITabBarController *)topViewController;
         topViewController = tab.selectedViewController;
     } else {
         break;
     }
 }

Swift 4+

extension UIWindow {
    func topViewController() -> UIViewController? {
        var top = self.rootViewController
        while true {
            if let presented = top?.presentedViewController {
                top = presented
            } else if let nav = top as? UINavigationController {
                top = nav.visibleViewController
            } else if let tab = top as? UITabBarController {
                top = tab.selectedViewController
            } else {
                break
            }
        }
        return top
    }
}
Yuchen Zhong
sumber
2
Saya menamainya visibleViewControlleruntuk memperjelas apa yang dilakukannya.
Jonny
31

Mendapatkan pengontrol tampilan terbanyak untuk Swift menggunakan ekstensi

Kode:

extension UIViewController {
    @objc func topMostViewController() -> UIViewController {
        // Handling Modal views
        if let presentedViewController = self.presentedViewController {
            return presentedViewController.topMostViewController()
        }
        // Handling UIViewController's added as subviews to some other views.
        else {
            for view in self.view.subviews
            {
                // Key property which most of us are unaware of / rarely use.
                if let subViewController = view.next {
                    if subViewController is UIViewController {
                        let viewController = subViewController as! UIViewController
                        return viewController.topMostViewController()
                    }
                }
            }
            return self
        }
    }
}

extension UITabBarController {
    override func topMostViewController() -> UIViewController {
        return self.selectedViewController!.topMostViewController()
    }
}

extension UINavigationController {
    override func topMostViewController() -> UIViewController {
        return self.visibleViewController!.topMostViewController()
    }
}

Pemakaian:

UIApplication.sharedApplication().keyWindow!.rootViewController!.topMostViewController()
Varuna
sumber
luar biasa - terima kasih banyak atas solusi ini. Trik subviews diperlukan! Sekali lagi, terima kasih banyak, Anda menyelamatkan hari saya.
iKK
25

Untuk melengkapi jawaban Eric (yang mengabaikan popovers, pengontrol navigasi, pengontrol tabbar, pengontrol tampilan ditambahkan sebagai subview ke beberapa pengontrol tampilan lainnya saat melintasi), ini adalah versi saya untuk mengembalikan pengontrol tampilan yang saat ini terlihat:

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

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)viewController {
    if ([viewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)viewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([viewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navContObj = (UINavigationController*)viewController;
        return [self topViewControllerWithRootViewController:navContObj.visibleViewController];
    } else if (viewController.presentedViewController && !viewController.presentedViewController.isBeingDismissed) {
        UIViewController* presentedViewController = viewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    }
    else {
        for (UIView *view in [viewController.view subviews])
        {
            id subViewController = [view nextResponder];
            if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
            {
                if ([(UIViewController *)subViewController presentedViewController]  && ![subViewController presentedViewController].isBeingDismissed) {
                    return [self topViewControllerWithRootViewController:[(UIViewController *)subViewController presentedViewController]];
                }
            }
        }
        return viewController;
    }
}

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

Dan sekarang yang perlu Anda lakukan untuk mendapatkan sebagian besar view controller adalah memanggil metode di atas sebagai berikut:

UIViewController *topMostViewControllerObj = [self topViewController];
Rajesh
sumber
Hilang SplitViewController juga?
apinho
21

Jawaban ini mencakup childViewControllersdan memelihara implementasi yang bersih dan mudah dibaca.

+ (UIViewController *)topViewController
{
    UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

    return [rootViewController topVisibleViewController];
}

- (UIViewController *)topVisibleViewController
{
    if ([self isKindOfClass:[UITabBarController class]])
    {
        UITabBarController *tabBarController = (UITabBarController *)self;
        return [tabBarController.selectedViewController topVisibleViewController];
    }
    else if ([self isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)self;
        return [navigationController.visibleViewController topVisibleViewController];
    }
    else if (self.presentedViewController)
    {
        return [self.presentedViewController topVisibleViewController];
    }
    else if (self.childViewControllers.count > 0)
    {
        return [self.childViewControllers.lastObject topVisibleViewController];
    }

    return self;
}
Luar biasa
sumber
Diperbarui beberapa kode, karena juga menunjukkan pengendali apa itu dengan meminimalkan dan mengembalikannya lagi. nik-kov-ios-developer.blogspot.ru/2016/12/…
Nik Kov
Hei, ayolah, di mana "topVisibleViewController" Anda?
Surga
12

Saya baru-baru ini mendapatkan situasi ini dalam satu proyek saya, yang diperlukan untuk menampilkan tampilan pemberitahuan apa pun yang ditampilkan oleh pengontrol dan apa pun jenisnya (UINavigationController, pengontrol klasik atau pengontrol tampilan kustom), ketika status jaringan berubah.

Jadi saya baru saja merilis kode saya, yang cukup mudah dan sebenarnya didasarkan pada protokol sehingga fleksibel dengan setiap jenis pengontrol wadah. Tampaknya terkait dengan jawaban terakhir, tetapi dengan cara yang fleksibel.

Anda dapat mengambil kode di sini: PPTopMostController

Dan mendapat pengontrol paling atas menggunakan

UIViewController *c = [UIViewController topMostController];
ipodishima
sumber
10

Ini adalah peningkatan dari jawaban Eric:

UIViewController *_topMostController(UIViewController *cont) {
    UIViewController *topController = cont;

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    if ([topController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visible = ((UINavigationController *)topController).visibleViewController;
        if (visible) {
            topController = visible;
        }
    }

    return (topController != cont ? topController : nil);
}

UIViewController *topMostController() {
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

    UIViewController *next = nil;

    while ((next = _topMostController(topController)) != nil) {
        topController = next;
    }

    return topController;
}

_topMostController(UIViewController *cont) adalah fungsi pembantu.

Sekarang yang perlu Anda lakukan hanyalah menelepon topMostController()dan yang paling top UIViewController harus dikembalikan!

JonasG
sumber
7
Sejak 1983 saya akan mengatakan. Ingat bahwa Objective-C mengandung C ... Membungkus kode ObjC dalam fungsi C adalah praktik yang umum, jadi ya, ini adalah kode Objective-C.
JonasG
@ JonasG Hai Jonas, Dalam keadaan apa Anda lebih suka membungkus kode ObjC dalam C? Karena, saya terkadang melihat fungsi C seperti ini dan tidak bisa membedakan penggunaan. Apakah kode pembungkus dalam C memberikan manfaat kinerja?
OzBoz
1
@OzBoz Dalam situasi di mana tidak jelas kelas mana yang selfseharusnya dimiliki.
adib
8

Ini pendapat saya tentang ini. Terima kasih kepada @Stakenborg karena telah menunjukkan cara untuk melewatkan mendapatkan UIAlertView sebagai pengontrol terbanyak

-(UIWindow *) returnWindowWithWindowLevelNormal
{
    NSArray *windows = [UIApplication sharedApplication].windows;
    for(UIWindow *topWindow in windows)
    {
        if (topWindow.windowLevel == UIWindowLevelNormal)
            return topWindow;
    }
    return [UIApplication sharedApplication].keyWindow;
}

-(UIViewController *) getTopMostController
{
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal)
    {
        topWindow = [self returnWindowWithWindowLevelNormal];
    }

    UIViewController *topController = topWindow.rootViewController;
    if(topController == nil)
    {
        topWindow = [UIApplication sharedApplication].delegate.window;
        if (topWindow.windowLevel != UIWindowLevelNormal)
        {
            topWindow = [self returnWindowWithWindowLevelNormal];
        }
        topController = topWindow.rootViewController;
    }

    while(topController.presentedViewController)
    {
        topController = topController.presentedViewController;
    }

    if([topController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *nav = (UINavigationController*)topController;
        topController = [nav.viewControllers lastObject];

        while(topController.presentedViewController)
        {
            topController = topController.presentedViewController;
        }
    }

    return topController;
}
Kamran Khan
sumber
Anda harus menghindari metode penamaan seperti getSomething:pada Objective-C. Ini memiliki arti khusus (lebih lanjut: cocoadevcentral.com/articles/000082.php ) dan Anda tidak memenuhi persyaratan ini dalam kode Anda.
Vive
7
@ implementation UIWindow (Ekstensi)

- (UIViewController *) topMostController
{
    UIViewController * topController = [self rootViewController];

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    kembali topController;
}

@akhir
FishStix
sumber
Saya tidak berpikir Anda puas dengan kondisi yang tercantum dalam posting asli.
Hot Licks
7
- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}
lifuqing_ios
sumber
Saya menggunakan ini, tetapi perhatikan bahwa itu rusak ketika ada lebih dari satu pengendali tampilan yang disajikan
Chuck Boris
7

Untuk Versi Swift terbaru:
Buat file, beri nama UIWindowExtension.swiftdan rekatkan cuplikan berikut:

import UIKit

public extension UIWindow {
    public var visibleViewController: UIViewController? {
        return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
    }

    public static func getVisibleViewControllerFrom(vc: UIViewController?) -> UIViewController? {
        if let nc = vc as? UINavigationController {
            return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
        } else if let tc = vc as? UITabBarController {
            return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
        } else {
            if let pvc = vc?.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(pvc)
            } else {
                return vc
            }
        }
    }
}

func getTopViewController() -> UIViewController? {
    let appDelegate = UIApplication.sharedApplication().delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}

Gunakan di mana saja sebagai:

if let topVC = getTopViewController() {

}
Ashok
sumber
Saya tidak ingin mengubah jawaban Anda terlalu banyak tetapi akan menyarankan beberapa hal. 1. Tambahkan dukungan untuk UISplitViewController. 2. gunakan switchalih-alih jika lain. 3. tidak yakin Anda memerlukan fungsi statis juga, saya pikir Anda bisa melakukan ini dengan mudah di tingkat contoh pertama yang Anda nyatakan. 4. Mungkin yang terbaik adalah tidak membuat terlalu banyak fungsi global tapi itu masalah selera. Anda dapat menggunakan satu baris kode untuk mencapai efek fungsi global:UIApplication.sharedApplication().delegate?.window?.visibleViewController
Jordan Smith
7

Ekstensi sederhana untuk UIApplicationdi Swift:

CATATAN:

Itu peduli di moreNavigationControllerdalamUITabBarController

extension UIApplication {

    class func topViewController(baseViewController: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {

        if let navigationController = baseViewController as? UINavigationController {
            return topViewController(navigationController.visibleViewController)
        }

        if let tabBarViewController = baseViewController as? UITabBarController {

            let moreNavigationController = tabBarViewController.moreNavigationController

            if let topViewController = moreNavigationController.topViewController where topViewController.view.window != nil {
                return topViewController(topViewController)
            } else if let selectedViewController = tabBarViewController.selectedViewController {
                return topViewController(selectedViewController)
            }
        }

        if let splitViewController = baseViewController as? UISplitViewController where splitViewController.viewControllers.count == 1 {
            return topViewController(splitViewController.viewControllers[0])
        }

        if let presentedViewController = baseViewController?.presentedViewController {
            return topViewController(presentedViewController)
        }

        return baseViewController
    }
}

Penggunaan sederhana:

if let topViewController = UIApplication.topViewController() {
    //do sth with top view controller
}
Bartłomiej Semańczyk
sumber
YA YA YA - ada banyak solusi di web untuk menemukan topMostViewController tetapi jika aplikasi Anda memiliki bilah tab dengan tab Lainnya, ANDA HARUS menanganinya sedikit berbeda.
Andy Obusek
7

Gunakan ekstensi di bawah ini untuk mengambil yang terlihat saat ini UIViewController. Bekerja untuk Swift 4.0 dan yang lebih baru

Swift 4.0 dan kemudian:

extension UIApplication {
    
    class func topViewController(_ viewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = viewController as? UINavigationController {
            return topViewController(nav.visibleViewController)
        }
        if let tab = viewController as? UITabBarController {
            if let selected = tab.selectedViewController {
                return topViewController(selected)
            }
        }
        if let presented = viewController?.presentedViewController {
            return topViewController(presented)
        }
        return viewController
    }
}

Cara Penggunaan?

let objViewcontroller = UIApplication.topViewController()
COVID-19
sumber
Bukankah seharusnya tes ini untuk presentedViewControllerpertama, sebelum UINavigationControllerdan UITabBarControllerkasus? Jika tidak, jika view controller ditampilkan secara modular dari UINavigationControlleratau UITabBarController, itu tidak akan dikembalikan sebagai pengendali tampilan atas, meskipun itu adalah view controller yang terlihat.
Drew
4

Namun solusi Swift lainnya

func topController() -> UIViewController? {

    // recursive follow
    func follow(from:UIViewController?) -> UIViewController? {
        if let to = (from as? UITabBarController)?.selectedViewController {
            return follow(to)
        } else if let to = (from as? UINavigationController)?.visibleViewController {
            return follow(to)
        } else if let to = from?.presentedViewController {
            return follow(to)
        }
        return from
    }

    let root = UIApplication.sharedApplication().keyWindow?.rootViewController

    return follow(root)

}
Martin Algesten
sumber
4

Ekstensi Swift 4.2


extension UIApplication {

    class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let navigationController = controller as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(controller: selected)
            }
        }
        if let presented = controller?.presentedViewController {


            return topViewController(controller: presented)
        }
        return controller
    }
}

Gunakan dari mana saja seperti,

 UIApplication.topViewController()?.present(yourController, animated: true, completion: nil)

atau seperti,

 UIApplication.topViewController()?
                    .navigationController?
                    .popToViewController(yourController,
                                         animated: true)

Sesuaikan dengan kelas apa saja seperti UINavigationController, UITabBarController

Nikmati!

Saranjith
sumber
1
@BilalBakhrom mengatakan "Dipilih. Saya pikir jawaban Anda adalah yang terbaik. Anda tidak dapat memanggil langsung metode topViewController (). Kelas aplikasi UIA adalah singleton, gunakan contoh yang disebut" dibagi "." dalam edit yang saya pilih untuk ditolak. Jika ini benar, silakan klik di sini untuk dibawa ke menu tempat Anda dapat menyetujui suntingan itu.
wizzwizz4
3

Inilah yang bekerja untuk saya.

Saya menemukan bahwa kadang-kadang controller nihil pada jendela kunci, karena keyWindow adalah beberapa hal OS seperti peringatan, dll.

 + (UIViewController*)topMostController
 {
     UIWindow *topWndow = [UIApplication sharedApplication].keyWindow;
     UIViewController *topController = topWndow.rootViewController;

     if (topController == nil)
     {
         // The windows in the array are ordered from back to front by window level; thus,
         // the last window in the array is on top of all other app windows.
         for (UIWindow *aWndow in [[UIApplication sharedApplication].windows reverseObjectEnumerator])
         {
             topController = aWndow.rootViewController;
             if (topController)
                 break;
         }
     }

     while (topController.presentedViewController) {
         topController = topController.presentedViewController;
     }

     return topController;
 }
Tom Andersen
sumber
3

Memperluas jawaban @ Eric, Anda harus berhati-hati bahwa keyWindow sebenarnya adalah jendela yang Anda inginkan. Jika Anda mencoba menggunakan metode ini setelah mengetuk sesuatu dalam tampilan peringatan misalnya, keyWindow sebenarnya akan menjadi jendela peringatan, dan itu akan menyebabkan masalah bagi Anda tidak diragukan lagi. Ini terjadi pada saya di alam liar ketika menangani tautan dalam melalui peringatan dan menyebabkan SIGABRT tanpa jejak STACK. Jalang total untuk debug.

Berikut kode yang saya gunakan sekarang:

- (UIViewController *)getTopMostViewController {
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal) {
        NSArray *windows = [UIApplication sharedApplication].windows;
        for(topWindow in windows)
        {
            if (topWindow.windowLevel == UIWindowLevelNormal)
                break;
        }
    }

    UIViewController *topViewController = topWindow.rootViewController;

    while (topViewController.presentedViewController) {
        topViewController = topViewController.presentedViewController;
    }

    return topViewController;
}

Jangan ragu untuk menggabungkan ini dengan rasa apa pun untuk mengambil pengontrol tampilan atas yang Anda sukai dari jawaban lain pada pertanyaan ini.

Stakenborg
sumber
Sudahkah Anda menemukan ini sebagai solusi yang lengkap? Begitu banyak jawaban lain yang sangat rumit, mencoba menjelaskan begitu banyak kasus tepi. Saya ingin ini benar, sangat sederhana dan elegan.
Le Mot Juiced
Saya tidak pernah punya masalah dengan itu. Jika Anda tidak melakukan sesuatu yang tidak biasa dengan nav stack, ini akan berhasil, jika tidak, beberapa solusi lain menangani kasus yang lebih rumit.
Stakenborg
3

Solusi Swift Alternatif:

static func topMostController() -> UIViewController {
    var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
    while (topController?.presentedViewController != nil) {
        topController = topController?.presentedViewController
    }

    return topController!
}
Esqarrouth
sumber
3

Solusi ini paling lengkap. Perlu dipertimbangkan: UINavigationController UIPageViewController UITabBarController Dan pengendali tampilan yang disajikan paling atas dari pengontrol tampilan atas

Contohnya adalah di Swift 3.

Ada 3 kelebihan

//Get the topmost view controller for the current application.
public func MGGetTopMostViewController() -> UIViewController?  {

    if let currentWindow:UIWindow = UIApplication.shared.keyWindow {
        return MGGetTopMostViewController(fromWindow: currentWindow)
    }

    return nil
}

//Gets the topmost view controller from a specific window.
public func MGGetTopMostViewController(fromWindow window:UIWindow) -> UIViewController? {

    if let rootViewController:UIViewController = window.rootViewController
    {
        return MGGetTopMostViewController(fromViewController:  rootViewController)
    }

    return nil
}


//Gets the topmost view controller starting from a specific UIViewController
//Pass the rootViewController into this to get the apps top most view controller
public func MGGetTopMostViewController(fromViewController viewController:UIViewController) -> UIViewController {

    //UINavigationController
    if let navigationViewController:UINavigationController = viewController as? UINavigationController {
        let viewControllers:[UIViewController] = navigationViewController.viewControllers
        if navigationViewController.viewControllers.count >= 1 {
            return MGGetTopMostViewController(fromViewController: viewControllers[viewControllers.count - 1])
        }
    }

    //UIPageViewController
    if let pageViewController:UIPageViewController = viewController as? UIPageViewController {
        if let viewControllers:[UIViewController] = pageViewController.viewControllers {
            if viewControllers.count >= 1 {
                return MGGetTopMostViewController(fromViewController: viewControllers[0])
            }
        }
    }

    //UITabViewController
    if let tabBarController:UITabBarController = viewController as? UITabBarController {
        if let selectedViewController:UIViewController = tabBarController.selectedViewController {
            return MGGetTopMostViewController(fromViewController: selectedViewController)
        }
    }

    //Lastly, Attempt to get the topmost presented view controller
    var presentedViewController:UIViewController! = viewController.presentedViewController
    var nextPresentedViewController:UIViewController! = presentedViewController?.presentedViewController

    //If there is a presented view controller, get the top most prensentedViewController and return it.
    if presentedViewController != nil {
        while nextPresentedViewController != nil {

            //Set the presented view controller as the next one.
            presentedViewController = nextPresentedViewController

            //Attempt to get the next presented view controller
            nextPresentedViewController = presentedViewController.presentedViewController
        }
        return presentedViewController
    }

    //If there is no topmost presented view controller, return the view controller itself.
    return viewController
}
Marc Renaud
sumber
3

Solusi ringkas namun komprehensif dalam Swift 4.2, dengan mempertimbangkan UINavigationControllers , UITabBarControllers , pengendali yang dihadirkan dan tampilan anak :

extension UIViewController {
  func topmostViewController() -> UIViewController {
    if let navigationVC = self as? UINavigationController,
      let topVC = navigationVC.topViewController {
      return topVC.topmostViewController()
    }
    if let tabBarVC = self as? UITabBarController,
      let selectedVC = tabBarVC.selectedViewController {
      return selectedVC.topmostViewController()
    }
    if let presentedVC = presentedViewController {
      return presentedVC.topmostViewController()
    }
    if let childVC = children.last {
      return childVC.topmostViewController()
    }
    return self
  }
}

extension UIApplication {
  func topmostViewController() -> UIViewController? {
    return keyWindow?.rootViewController?.topmostViewController()
  }
}

Pemakaian:

let viewController = UIApplication.shared.topmostViewController()
nalexn
sumber
2

Solusi hebat di Swift, implementasikan di AppDelegate

func getTopViewController()->UIViewController{
    return topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
}
func topViewControllerWithRootViewController(rootViewController:UIViewController)->UIViewController{
    if rootViewController is UITabBarController{
        let tabBarController = rootViewController as! UITabBarController
        return topViewControllerWithRootViewController(tabBarController.selectedViewController!)
    }
    if rootViewController is UINavigationController{
        let navBarController = rootViewController as! UINavigationController
        return topViewControllerWithRootViewController(navBarController.visibleViewController)
    }
    if let presentedViewController = rootViewController.presentedViewController {
        return topViewControllerWithRootViewController(presentedViewController)
    }
    return rootViewController
}
Edward Novelo
sumber
1

Saya pikir sebagian besar jawaban telah sepenuhnya diabaikan UINavigationViewController, jadi saya menangani use case ini dengan mengikuti implementasi.

+ (UIViewController *)topMostController {
    UIViewController * topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController || [topController isMemberOfClass:[UINavigationController class]]) {
        if([topController isMemberOfClass:[UINavigationController class]]) {
            topController = [topController childViewControllers].lastObject;
        } else {
            topController = topController.presentedViewController;
        }
    }

    return topController;
}
Aamir
sumber
1

Saya tahu ini sangat terlambat dan mungkin berlebihan. Tetapi berikut ini cuplikan yang saya buat yang bekerja untuk saya:

    static func topViewController() -> UIViewController? {
        return topViewController(vc: UIApplication.shared.keyWindow?.rootViewController)
    }

    private static func topViewController(vc:UIViewController?) -> UIViewController? {
        if let rootVC = vc {
            guard let presentedVC = rootVC.presentedViewController else {
                return rootVC
            }
            if let presentedNavVC = presentedVC as? UINavigationController {
                let lastVC = presentedNavVC.viewControllers.last
                return topViewController(vc: lastVC)
            }
            return topViewController(vc: presentedVC)
        }
        return nil
    }
Anil Arigela
sumber
0

Ini berfungsi baik untuk menemukan viewController 1 teratas dari kontrol tampilan root apa pun

+ (UIViewController *)topViewControllerFor:(UIViewController *)viewController
{
    if(!viewController.presentedViewController)
        return viewController;
    return [MF5AppDelegate topViewControllerFor:viewController.presentedViewController];
}

/* View Controller for Visible View */

AppDelegate *app = [UIApplication sharedApplication].delegate;
UIViewController *visibleViewController = [AppDelegate topViewControllerFor:app.window.rootViewController]; 
johnnyg17
sumber
0

Tidak yakin apakah ini akan membantu apa yang Anda coba selesaikan dengan menemukan pengontrol tampilan teratas, tapi saya mencoba menyajikan pengontrol tampilan baru, tetapi jika pengontrol tampilan root saya sudah memiliki dialog modal, itu akan diblokir, jadi saya akan berputar ke atas dari semua pengontrol tampilan modal menggunakan kode ini:

UIViewController* parentController =[UIApplication sharedApplication].keyWindow.rootViewController;

while( parentController.presentedViewController &&
       parentController != parentController.presentedViewController )
{
    parentController = parentController.presentedViewController;
}
Toland Hon
sumber
0

Anda dapat menemukan pengontrol tampilan paling atas dengan menggunakan

NSArray *arrViewControllers=[[self navigationController] viewControllers];
UIViewController *topMostViewController=(UIViewController *)[arrViewControllers objectAtIndex:[arrViewControllers count]-1];
Tapas Pal
sumber
Kecuali itu, jika Anda benar-benar membaca pertanyaan, selftidak memiliki navigationControllerproperti.
Hot Licks
0

Solusi lain bergantung pada rantai responden, yang mungkin atau mungkin tidak bekerja tergantung pada apa responden pertama adalah:

  1. Dapatkan responden pertama .
  2. Dapatkan UIViewController terkait dengan responden pertama itu .

Contoh kode semu:

+ (UIViewController *)currentViewController {
    UIView *firstResponder = [self firstResponder]; // from the first link above, but not guaranteed to return a UIView, so this should be handled more appropriately.
    UIViewController *viewController = [firstResponder viewController]; // from the second link above
    return viewController;
}
Masuk akal
sumber
0

Cepat:

extension UIWindow {

func visibleViewController() -> UIViewController? {
    if let rootViewController: UIViewController  = self.rootViewController {
        return UIWindow.getVisibleViewControllerFrom(rootViewController)
    }
    return nil
}

class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
if vc.isKindOfClass(UINavigationController.self) {

    let navigationController = vc as UINavigationController
    return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)

} else if vc.isKindOfClass(UITabBarController.self) {

    let tabBarController = vc as UITabBarController
    return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController!)

} else {

    if let presentedViewController = vc.presentedViewController {

        return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)

    } else {

        return vc;
    }
}
}

Pemakaian:

 if let topController = window.visibleViewController() {
            println(topController)
        }
Bobj-C
sumber