Get visibleViewController using UIWindow? [duplicate] - ios

This question already has answers here:
How to find topmost view controller on iOS
(42 answers)
Closed 5 years ago.
So, I have self as UIWindow, but how can I get visibleViewController at current moment?

IN swift3:
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}

You should check out this answer. The gist of it is that you start with the window's .rootViewController. In my own code (using a UINavigationController as the .rootViewController, I use this (in AppDelegate):
if let nvc = self.window?.rootViewController as? UINavigationController {
if let mvc = nvc.topViewController as? MasterViewController {
// ... do something
} else if let dvc = nvc.topViewController as? DetailViewController {
// ... do something
}
}
Note that if you are using the default template for a Master-Detail application, you will need to consider the SplitViewController which interposes itself, but that should be reasonably obvious from the boilerplate code.

If you add the child view controller:
let viewControllersVisible = self.rootViewController?.childViewControllers.filter({ $0.isVisible && $0.view.window })
This returns an array of UIViewControllers added in your view hierarchy, it doesn't say if the user is actually able to see those view controllers, depends on your hierarchy.
if you present modally just a view controller:
let viewControllerVisible = self.rootViewController?.presentedViewController

Inorder to get a reference to the top most view controller in the hierarchy try the following code
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController)
{
topController = topController.presentedViewController;
}
return topController;

If you want the topmost view on window try with this you will get the view.
[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

Related

Correct way to grab the current navigation controller

Given that I have rootViewController which is UIApplication.shared.delegate?.window??.rootViewController, I want to grab the active navigation controller, if any.
So far what I've come up with:
guard var controller = rootViewController?.presentedViewController else { return rootViewController as? UINavigationController }
while let presented = controller.presentedViewController {
controller = presented
}
controller = controller.navigationController ?? controller
return controller as? UINavigationController
Is this sufficient? A co-working gave me this solution but the part I don't understand is rootViewController?.presentedViewController. Shouldn't it be rootViewController?.presentingViewController?
Use the below extension to grab the top most or current visible UIViewController and UINavigationController.
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
}
class func topNavigationController(_ viewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UINavigationController? {
if let nav = viewController as? UINavigationController {
return nav
}
if let tab = viewController as? UITabBarController {
if let selected = tab.selectedViewController {
return selected.navigationController
}
}
return viewController?.navigationController
}
}
How to use?
let objViewcontroller = UIApplication.topViewController()
OR
let objNavigationController = UIApplication.topNavigation()
Is this sufficient?
Only you know that. Does it work reliably in your situation? If yes, it might be sufficient for your needs. But it's not the generally correct answer.
A much more reliable method is, as #vivekDas mentioned in a comment, to use the navigationController method, which returns the nearest view controller in the graph that's a navigation controller.
...the part I don't understand is rootViewController?.presentedViewController. Shouldn't it be rootViewController?.presentingViewController?
No. Let's say you've got two view controllers, a and b, and a presents b. In that case, a.presentedViewController is b, and b.presentingViewController is a. So rootViewController.presentedViewController is the view controller that rootViewController presents. rootViewController.presentingViewController would be the controller that presented rootViewController. But rootViewController is the root of the view controller object graph; by definition it hasn't been presented by any other view controller, so rootViewController.presentingViewController will always be nil.

After closeing MMDrawer Controller which view controller life cycle method will called in swift4?

i have a tab bar controller, and inside that tab bar i have five view controller. i also have have a right view controller(drawer) which is on homeVC. for rightViewController i use MMDrawerController source: https://www.youtube.com/watch?time_continue=1468&v=TdKnImb4SWs. in the appdelegate . Now when the right view controller is closed, which life cycle of view controller is getting called? because i want to write a function. I have used this code on appdelegate
let rightViewController = mainStoryboard.instantiateViewController(withIdentifier: "RightViewVC") as! RightViewVC
UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
let rightSideMenuNav = UINavigationController(rootViewController: rightViewController)
drawerController = MMDrawerController(center:mainPage,rightDrawerViewController:rightSideMenuNav)
// drawerController!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.panningCenterView
drawerController!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.panningCenterView
drawerController!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.all
drawerController!.preferredInterfaceOrientationForPresentation = MMDrawerOpenCenterInteractionMode.full
window?.rootViewController = drawerController
MMDrawer provide the completion block when open,close or toggel the drawer you just need to compare the event like drawer is close or open you got the success. Here is the code
#IBAction func rightMenuButtonTapped(_ sender: Any) {
let appdelegate = UIApplication.shared.delegate as! AppDelegate
appdelegate.drawerController?.toggle(MMDrawerSide.right, animated: true){ (isCompleted) in
if appdelegate.drawerController?.openSide == MMDrawerSide.none
{
print("drawer close")
if let objHome = appdelegate.drawerController?.centerViewController as? HomeVC{
print("Got Home VC")
}
}
else if appdelegate.drawerController?.openSide == MMDrawerSide.right
{
print("drawer open")
}
}
}
the viewwillApear() in homeViewController will get called when rightViewController closed.
Try to print in each viewDidLoad to see the active controllers:
if let window = UIApplication.shared.delegate?.window {
if var viewController = window?.rootViewController {
// handle navigation controllers
if(viewController is UINavigationController){
viewController = (viewController as! UINavigationController).visibleViewController!
}
print(viewController)
}
}
or
if self.window.rootViewController is MyViewController {
//do something if it's an instance of that class
}

Can a navigation controller and its top view controller have a presented view controller at the same time?

I need to find the top most navigation controller of my view controller hierarchy. I couldn't figure for sure if a navigation controller AND it's top view controller can have presented view controllers at the same, i.e
NavigationController --Presented--> UIViewController A
|
|
NavigationController.topViewController --Presented--> UIViewController B
Is this possible simultaneously? As in would i have to traverse both paths to the end and compare which is longer and then choose the correct path?
What I tried
I tried to simultaneously present view controllers on a navigation controller and its top view controller but i get this warning in LLDB
"Attempt to present on whose view is not in the window hierarchy!"
It didn't present the view controller (0x100605860) but will this ALWAYS be the case? Can custom presentations leave a view in the window hierarchy?
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.
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
}
}
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
}
}
You can you use this anywhere on your controller
if let topController = UIApplication.topViewController() {
}

How do I get the top view which is a presented view controller

I have a navigation bar controller. After some action i need to pop up a new view which is a presented view controller. Then How do i get the top view ?
I always getting top view on navigation stack but not the presented view. Why?
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let aVariable = appDelegate.window
if let topController = aVariable!.visibleViewController() {
print(topController)
}
Try
if let topController = myNavigationController.visibleViewController {
print(topController)
}
Swift 3 example to get top UIViewController to present another UIViewController:
import UIKit
extension UIApplication {
var topVC: UIViewController? {
get {
let rootVC = UIApplication.shared.keyWindow?.rootViewController
if let nav = rootVC as? UINavigationController {
return nav.visibleViewController
} else if let tab = rootVC as? UITabBarController, let selected = tab.selectedViewController {
return selected
} else if let presented = rootVC?.presentedViewController {
return presented
}
return rootVC
}
}
}
Can call it with:
_ = UIApplication.shared.topVC?.present(yourViewController, animated: true)

iOS top level navigation controller from AppDelegate Swift

I am using this line:
var rootViewController = self.window!.rootViewController as? UINavigationController
However, this only returns the root and not the top level nav controller. I am trying to push a view on top, but if the root controller is not the top level, The view is not on top.
I am trying to find out the navigation controller of the current view controller.
This is all to solve my problem of this error:
Warning: Attempt to present <UINavigationController: 0x7f8473d7c800> on <UINavigationController: 0x7f8472022600> whose view is not in the window hierarchy!
Any suggestions?
You're nearly there :
func topViewController() -> UIViewController? {
if let rootViewController = self.window?.rootViewController as? UINavigationController {
return rootViewController.topViewController
}
return nil
}
Just be careful because there might also be another ViewController presented modally on top of your topViewController. You might want to check the topViewController.presentedViewController as well :
func topViewController() -> UIViewController? {
if let rootViewController = self.window?.rootViewController as? UINavigationController {
if let topVC = rootViewController.topViewController {
if let modalTopVC = topVC.presentedViewController {
return modalTopVC
}
return topVC
}
return rootViewController
}
return nil
}

Resources