I can\'t seem to get the top most UIViewController
without access to a UINavigationController
. Here is what I have so far:
UIApplic
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
}
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)
}
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;
}
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()
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.
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
}
}
}