Get top most UIViewController
Asked Answered
P

29

252

I can't seem to get the top most UIViewController without access to a UINavigationController. Here is what I have so far:

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(vc, animated: true, completion: nil)

However, it does not seem to do anything. The keyWindow and rootViewController seem to be non-nil values too, so the optional chaining shouldn't be an issue.

NOTE: It is a bad idea to do something like this. It breaks the MVC pattern.

Pentup answered 31/10, 2014 at 3:14 Comment(2)
Here is one alternative solution available https://mcmap.net/q/111573/-show-uialertcontroller-if-already-showing-an-alertBeora
related: Get the viewController a uiView is onCyanine
U
395

presentViewController shows a view controller. It doesn't return a view controller. If you're not using a UINavigationController, you're probably looking for presentedViewController and you'll need to start at the root and iterate down through the presented views.

if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}

For Swift 3+:

if var topController = UIApplication.shared.keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}

For iOS 13+

let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

if var topController = keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

// topController should now be your topmost view controller
}
Underdrawers answered 31/10, 2014 at 3:29 Comment(10)
Can someone explain the while loop? To me, it looks like there is nothing to loop over; I'm not even sure why this compiles.Griselgriselda
@ProfessorTom The loop continues as long as topController.presentedViewController returns something (i.e., the controller has a presented child controller). It's while let to enforce the fact that topController.presentedViewController must return something. If it returns nil (i.e., this is controller has no presented children), then it will stop looping. In the body of the loop, it reassigns the child as the current topController, and loops again, going down the view controller hierarchy. It can reassign topController as it's a var in the outer if statement.Underdrawers
thank you. I haven't been able to find any examples online of while let. There are, of course, plenty of if let example to be found.Griselgriselda
The let x = somethingThatCouldBeNil syntax is a super handy trick to use anywhere a truth value/condition could be used. If we didn't use it here, we'd have to explicitly assign a value, then test to see if it's actually there. I think it's really succinct and expressive.Underdrawers
I'm familiar with the trick, it's just a bit more difficult to reason about in while loops–for which I have found a dearth of examples–especially this one.Griselgriselda
This is not perfectCaty
@Alfi it works for me perfectly with the newest Swift and XcodeVacuole
@Underdrawers after exception can't able to get the topController name?Sassafras
I remember back in the WPF days, I had to loop through the visual tree to do similar things, so it's not as dirty as people think. Moreover, I like this answer more than the others for its use of a loop rather than a recursive function, since a recursive function might require a deep callstack, more memory, and is less efficient. Thank you for confirming this is the way. 🚀Hyperon
"'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead", can I say if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }) { /* existing code */ }?Lydell
T
309

have this extension

Swift 2.*

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

Swift 3

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
    }
}

You can then use this anywhere on your controller

if let topController = UIApplication.topViewController() {
    
}
Turner answered 16/6, 2015 at 4:6 Comment(6)
I attempted to make an important edit to this answer, but it got rejected (I have no idea why and the template reasons given didn't make sense): It's important to check if the nav.visibleViewController is nil before using it in the recursive call (just like how tab.selectedViewController is checked) because otherwise, if it was nil, you would get into a recursive infinite loop.Sucrose
@EthanG According to my understanding, if nav.visibleViewController is nil, the function will return nil (drop to the last return). How can it get into an infinite loop?Electromotive
I think it would be more logical to make this as static function of UIViewControllerLucais
The first three returns can return a nil value. To be sure it doesn't happen, an option would be to add ?? controller at the end of these returns. For instance, in the first one, to return this: return topViewController(controller: navigationController.visibleViewController) ?? controllerImparity
The 'presentedViewController' check should probably come first if you want to catch modally presented view controllers on UITabBarControllers..Responsiveness
@Responsiveness you are correct. Currently this implementation will not return the correct controller if a view controller is modally presented on a UINavigationController, or a UITabBarController. I also wonder what happens if a UIAlertController is presented...Troutman
A
94

For swift 4 / 5 + to get topmost viewController

// MARK: UIApplication extensions

extension UIApplication {

    class func getTopViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return getTopViewController(base: nav.visibleViewController)

        } else if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
            return getTopViewController(base: selected)

        } else if let presented = base?.presentedViewController {
            return getTopViewController(base: presented)
        }
        return base
    }
}

How to use

if let topVC = UIApplication.getTopViewController() {
   topVC.view.addSubview(forgotPwdView)
}
Andersen answered 2/6, 2018 at 11:3 Comment(1)
Change the parameter to: base: UIViewController? = UIApplication.shared.windows.first { $0.isKeyWindow }?.rootViewControllerDecrescendo
E
21
extension UIWindow {

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

    static func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
        if let navigationController = vc as? UINavigationController,
            let visibleController = navigationController.visibleViewController  {
            return UIWindow.getVisibleViewControllerFrom( vc: visibleController )
        } else if let tabBarController = vc as? UITabBarController,
            let selectedTabController = tabBarController.selectedViewController {
            return UIWindow.getVisibleViewControllerFrom(vc: selectedTabController )
        } else {
            if let presentedViewController = vc.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController)
            } else {
                return vc
            }
        }
    }
}

Usage:

if let topController = window.visibleViewController() {
    println(topController)
}
Equine answered 19/12, 2014 at 15:9 Comment(3)
this solution looked really promising, however I tried to run this to get the view controller I'm on when I receive a push notification and it threw a nil error on the return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)Crossbench
@Crossbench you need to only use presentedViewController, not presentedViewController. presentedViewControllerRant
@Rant If you did present a modal view controller on top of a modal view controller then you need .presentedViewController.presentedViewController, or not?Howdah
J
19

I use the following code to get the topViewController-

It is backward compatible to older iOS versions & takes care of UIScene too

extension UIApplication {
    func topViewController() -> UIViewController? {
        var topViewController: UIViewController? = nil
        if #available(iOS 13, *) {
            for scene in connectedScenes {
                if let windowScene = scene as? UIWindowScene {
                    for window in windowScene.windows {
                        if window.isKeyWindow {
                            topViewController = window.rootViewController
                        }
                    }
                }
            }
        } else {
            topViewController = keyWindow?.rootViewController
        }
        while true {
            if let presented = topViewController?.presentedViewController {
                topViewController = presented
            } else if let navController = topViewController as? UINavigationController {
                topViewController = navController.topViewController
            } else if let tabBarController = topViewController as? UITabBarController {
                topViewController = tabBarController.selectedViewController
            } else {
                // Handle any other third party container in `else if` if required
                break
            }
        }
        return topViewController
    }
}

It could be used in this way:

let topController = UIApplication.shared.topViewController()
topController?.present(controllerToPresent, animated: true, completion: nil)
Jeaz answered 10/3, 2021 at 21:28 Comment(4)
Upvoted! This did work even with SDL2 with C/Swift binding, where other answers did not. Great!Absolute
I'm glad it helped.Jeaz
why this line required "while true {" ?Andersen
@HardikThakkar It makes sure that we recursively check the parent of topViewController until we reach a state, where there is no more parent.Jeaz
S
8
import UIKit

extension UIApplication {

    // MARK: Choose keyWindow as per your choice
    var currentWindow: UIWindow? {
        connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
    }

    // MARK: Choose keyWindow as per your choice
    var keyWindow: UIWindow? {
        UIApplication.shared.windows.first { $0.isKeyWindow }
    }

    class func topMostViewController(base: UIViewController? = UIApplication.shared.currentWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return topMostViewController(base: nav.visibleViewController)
        }

        if let tab = base as? UITabBarController {
            let moreNavigationController = tab.moreNavigationController

            if let top = moreNavigationController.topViewController, top.view.window != nil {
                return topMostViewController(base: top)
            } else if let selected = tab.selectedViewController {
                return topMostViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topMostViewController(base: presented)
        }
        return base
    }
}
Swint answered 27/2, 2020 at 13:49 Comment(1)
Ambiguous use of 'visibleViewController'Westberg
C
8

iOS13+ //top Most view Controller

extension UIViewController {
    func topMostViewController() -> UIViewController {
        if self.presentedViewController == nil {
            return self
        }
        if let navigation = self.presentedViewController as? UINavigationController {
            return navigation.visibleViewController!.topMostViewController()
        }
        if let tab = self.presentedViewController as? UITabBarController {
            if let selectedTab = tab.selectedViewController {
                return selectedTab.topMostViewController()
            }
            return tab.topMostViewController()
        }
        return self.presentedViewController!.topMostViewController()
    }
}

extension UIApplication {
    func topMostViewController() -> UIViewController? {
        return UIWindow.key!.rootViewController?.topMostViewController()
    }
}

extension UIWindow {
    static var key: UIWindow? {
        if #available(iOS 13, *) {
            return UIApplication.shared.windows.first { $0.isKeyWindow }
        } else {
            return UIApplication.shared.keyWindow
        }
    }
}

//use let vc = UIApplication.shared.topMostViewController()

// End top Most view Controller
Cobos answered 11/10, 2020 at 7:46 Comment(0)
C
7

I loved @dianz's answer, and so here is the swift 3's version of it. It's basically the same thing but his was missing a curly brace and some of the syntax/variable/method names have changed. So here it is!

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

Usage is still the exact same though:

if let topController = UIApplication.topViewController() {
    print("The view controller you're looking at is: \(topController)")
}
Claar answered 4/10, 2016 at 16:22 Comment(0)
N
6

Based on Dianz answer, the Objective-C version

- (UIViewController *) topViewController {
   UIViewController *baseVC = UIApplication.sharedApplication.keyWindow.rootViewController;
   if ([baseVC isKindOfClass:[UINavigationController class]]) {
       return ((UINavigationController *)baseVC).visibleViewController;
   }

   if ([baseVC isKindOfClass:[UITabBarController class]]) {
       UIViewController *selectedTVC = ((UITabBarController*)baseVC).selectedViewController;
       if (selectedTVC) {
           return selectedTVC;
       }
   }

   if (baseVC.presentedViewController) {
       return baseVC.presentedViewController;
   }
   return baseVC;
}
Nomenclature answered 11/2, 2016 at 6:25 Comment(2)
Won't work for UINavigationController in UITabBarController. it will return UINavigationController, should return the topController in navigation stuck.Parthenon
Tnx Tnx Tnx BroCaty
D
6

Use this code to find top most UIViewController

func getTopViewController() -> UIViewController? {
    var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
    while topController?.presentedViewController != nil {
        topController = topController?.presentedViewController
    }
    return topController
}
Determine answered 14/9, 2017 at 12:1 Comment(1)
How is this different from rickerbh's answer ?Red
H
6

https://gist.github.com/db0company/369bfa43cb84b145dfd8 I did some tests on the answers and comments on this site. For me, the following works

extension UIViewController {
    func topMostViewController() -> UIViewController {

        if let presented = self.presentedViewController {
            return presented.topMostViewController()
        }

        if let navigation = self as? UINavigationController {
            return navigation.visibleViewController?.topMostViewController() ?? navigation
        }

        if let tab = self as? UITabBarController {
            return tab.selectedViewController?.topMostViewController() ?? tab
    }

        return self
    }
}

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

Then, get the top viewController by:

UIApplication.shared.topMostViewController()
Honeysweet answered 10/8, 2018 at 14:22 Comment(0)
A
6

For Swift 5+, iOS 13+

extension UIViewController {
    static func topMostViewController() -> UIViewController? {
        if #available(iOS 13.0, *) {
            let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
            return keyWindow?.rootViewController?.topMostViewController()
        }
        
        return UIApplication.shared.keyWindow?.rootViewController?.topMostViewController()
    }
    
    func topMostViewController() -> UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.topMostViewController()
        }
        else if let tabBarController = self as? UITabBarController {
            if let selectedViewController = tabBarController.selectedViewController {
                return selectedViewController.topMostViewController()
            }
            return tabBarController.topMostViewController()
        }
            
        else if let presentedViewController = self.presentedViewController {
            return presentedViewController.topMostViewController()
        }
        
        else {
            return self
        }
    }
}

Usage:

When you are getting topMostViewController without instance of UIViewController

guard let viewController = UIViewController.topMostViewController() else { return }
print(viewController)

When you are getting topMostViewController of instance of UIViewController

let yourVC = UIViewController()
guard let vc = yourVC.topMostViewController() else { return }
print(vc)
Alan answered 20/8, 2020 at 16:11 Comment(0)
W
5

Slight Variation on @AlberZou using a computed variable rather than a function

extension UIViewController {
  var topMostViewController : UIViewController {

    if let presented = self.presentedViewController {
      return presented.topMostViewController
    }

    if let navigation = self as? UINavigationController {
      return navigation.visibleViewController?.topMostViewController ?? navigation
    }

    if let tab = self as? UITabBarController {
      return tab.selectedViewController?.topMostViewController ?? tab
    }

    return self
  }
}

extension UIApplication {
  var topMostViewController : UIViewController? {
    return self.keyWindow?.rootViewController?.topMostViewController
  }
}

Then say

if let topViewControler = UIApplication.shared.topMostViewController {
    ... do stuff
}
Wollis answered 22/10, 2018 at 15:6 Comment(0)
D
5

in SWIFT 5.2

you can use underneath code:

import UIKit

extension UIWindow {
    static func getTopViewController() -> UIViewController? {
        if #available(iOS 13, *){
            let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
            
            if var topController = keyWindow?.rootViewController {
                while let presentedViewController = topController.presentedViewController {
                    topController = presentedViewController
                }
                return topController
            }
        } else {
            if var topController = UIApplication.shared.keyWindow?.rootViewController {
                while let presentedViewController = topController.presentedViewController {
                    topController = presentedViewController
                }
                return topController
            }
        }
        return nil
    }
}
Directoire answered 4/11, 2020 at 7:44 Comment(0)
M
4

Based on Bob -c above:

Swift 3.0

extension UIWindow {


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

    class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {

        if vc.isKind(of: UINavigationController.self) {

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

        } else if vc.isKind(of: UITabBarController.self) {

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

        } else {

            if let presentedViewController = vc.presentedViewController {

                return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController)

            } else {

                return vc;
            }
        }
    }
}
Marplot answered 19/4, 2017 at 8:51 Comment(0)
P
4

Too many flavours but none an iterative elaborated one. Combined from the previous ones:

     func topMostController() -> UIViewController? {
        var from = UIApplication.shared.keyWindow?.rootViewController
        while (from != nil) {
            if let to = (from as? UITabBarController)?.selectedViewController {
                from = to
            } else if let to = (from as? UINavigationController)?.visibleViewController {
                from = to
            } else if let to = from?.presentedViewController {
                from = to
            } else {
                break
            }
        }
        return from
    }
Provocation answered 12/4, 2019 at 9:47 Comment(0)
J
4

Swift 5+

Declare

func topViewController() -> UIViewController? {
    
    let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

    if var topController = keyWindow?.rootViewController {
        
        while let presentedViewController = topController.presentedViewController {
            topController = presentedViewController
        }
        
        return topController
        
    } else {
        
        return nil
        
    }
    
}

Use

topViewController()?.view.addSubview(UIView())
Judgment answered 23/3, 2021 at 6:10 Comment(0)
E
3

you can define a UIViewController variable in AppDelegate, and in every viewWillAppear set the variable to self.(however dianz answer is the best answer.)

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
    appDel.currentVC = self
}
Exequatur answered 19/1, 2017 at 8:31 Comment(2)
thanks a lot it work fine for me as the other solution when it try to get the navigationControll it return nil so i was't able to push any new vcInterpleader
Make sure the currentVC defined as weak reference, or you will have memory leak.Overthrust
M
3

iOS 13 and above, also handles with UIWindowScene and backward compatible old iOS versions

    extension UIApplication {
        
       public var mainKeyWindow: UIWindow? {
           if #available(iOS 13, *) {
             return UIApplication.shared.connectedScenes
            .filter { $0.activationState == .foregroundActive }
            .first(where: { $0 is UIWindowScene })
            .flatMap { $0 as? UIWindowScene }?.windows
            .first(where: \.isKeyWindow)
           } else {
              return UIApplication.shared.windows.first { $0.isKeyWindow }
           }
        }
    

    public var rootViewController: UIViewController? {
        guard let keyWindow = UIApplication.shared.mainKeyWindow, let rootViewController = keyWindow.rootViewController else {
            return nil
        }
        return rootViewController
    }

    public func topViewController(controller: UIViewController? = UIApplication.shared.rootViewController) -> UIViewController? {

        if controller == nil {
            return topViewController(controller: rootViewController)
        }
        
        if let navigationController = controller as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }

        if let tabController = controller as? UITabBarController {
            if let selectedViewController = tabController.selectedViewController {
                return topViewController(controller: selectedViewController)
            }
        }

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

        return controller
    }
}
Manxman answered 13/12, 2020 at 15:57 Comment(0)
I
2

To find the visible viewController in Swift 3

if let viewControllers = window?.rootViewController?.childViewControllers {

     let prefs = UserDefaults.standard

     if viewControllers[viewControllers.count - 1] is ABCController{
        print("[ABCController] is visible")

     }
}

This code find the last added or the last active controller visible.

This I have used in AppDelegate to find active view Controller

Index answered 29/3, 2017 at 6:46 Comment(0)
R
2
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
    }
Reames answered 21/8, 2020 at 9:50 Comment(0)
R
1

Where did you put the code in?

I try your code in my demo, I found out, if you put the code in

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 

will fail, because key window have been setting yet.

But I put your code in some view controller's

override func viewDidLoad() {

It just works.

Ranged answered 31/10, 2014 at 3:37 Comment(1)
It's not in didFinishLaunchingWithOptions. I just need this for various debug purposes.Pentup
U
1

In a very rare case, with custom segue, the top most view controller is not in a navigation stack or tab bar controller or presented, but its view is inserted to the top of key windown's subviews.

In such situation, it's necessary to check if UIApplication.shared.keyWindow.subviews.last == self.view to determine if the current view controller is the top most.

Urbani answered 24/1, 2019 at 11:58 Comment(0)
C
1
  var topViewController: UIViewController? {
        guard var topViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
        while let presentedViewController = topViewController.presentedViewController {
            topViewController = presentedViewController
        }
        return topViewController
    }
Councilman answered 13/6, 2019 at 7:18 Comment(0)
H
1

For anyone looking for a swift 5/iOS 13+ solution (keywindow is deprecated since iOS 13)

extension UIApplication {

    class func getTopMostViewController() -> UIViewController? {
        let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
        if var topController = keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }
            return topController
        } else {
            return nil
        }
    }
}
Humor answered 25/4, 2020 at 5:29 Comment(2)
How would I use it?Playground
Just call it like this. UIApplication.getTopMostViewController() inside your ViewController. @ChrisComasHumor
W
1

try this

let topVisibleVC = UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
Westberg answered 19/10, 2022 at 17:53 Comment(0)
N
0

The best solution for me is an extension with a function. Create a swift file with this extension

First is the UIWindow extension:

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

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

inside that file add function

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

And if you want to use it, you can call it anywhere. Example:

  override func viewDidLoad() {
    super.viewDidLoad()
      if let topVC = visibleViewController() {
             //show some label or text field 
    }
}

File code is like this:

import UIKit

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

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

func visibleViewController() -> UIViewController? {
    let appDelegate = UIApplication.shared.delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}
Nethermost answered 22/8, 2019 at 5:53 Comment(0)
G
0
extension UIViewController {
    func topMostViewController() -> UIViewController {
        if self.presentedViewController == nil {
            return self
        }
        if let navigation = self.presentedViewController as? UINavigationController {
            return navigation.visibleViewController.topMostViewController()
        }
        if let tab = self.presentedViewController as? UITabBarController {
            if let selectedTab = tab.selectedViewController {
                return selectedTab.topMostViewController()
            }
            return tab.topMostViewController()
        }
        return self.presentedViewController!.topMostViewController()
    }
}

extension UIApplication {
    func topMostViewController() -> UIViewController? {
        return self.keyWindow?.rootViewController?.topMostViewController()
    }
}
Gallego answered 20/7, 2020 at 5:27 Comment(0)
H
-2

Simplest way is to take the last one from the stack of view controllers:

if let viewController: UIViewController = navigationController.viewControllers.last {
    // This `viewController` is on the top.
}
Homan answered 21/6, 2022 at 11:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.