How to get visible viewController from app delegate when using storyboard?

后端 未结 14 1668
小蘑菇
小蘑菇 2020-12-05 00:14

I have some viewControllers, and I don\'t use NavigationController. How can I get visible view controller in app delegate methods (e.g. appli

相关标签:
14条回答
  • 2020-12-05 00:46

    A modified version of a previous answer using UIViewController category in ObjC:

    UIViewController+VisibleViewController.h

    #import <UIKit/UIKit.h>
    
    @interface UIViewController (VisibleViewController)
    
    - (UIViewController *)visibleViewController;
    
    @end
    

    UIViewController+VisibleViewController.m

    #import "UIViewController+VisibleViewController.h"
    
    @implementation UIViewController (VisibleViewController)
    
    - (UIViewController *)visibleViewController {
        if (self.presentedViewController == nil) {
            return self;
        }
        if ([self.presentedViewController isKindOfClass:[UINavigationController class]]) {
            UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
            UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
    
            return [lastViewController visibleViewController];
        }
        if ([self.presentedViewController isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tabBarController = (UITabBarController *)self.presentedViewController;
            UIViewController *selectedViewController = tabBarController.selectedViewController;
    
            return [selectedViewController visibleViewController];
        }
    
        UIViewController *presentedViewController = (UIViewController *)self.presentedViewController;
    
        return [presentedViewController visibleViewController];
    }
    
    @end
    

    AppDelegate.m

    #import "UIViewController+VisibleViewController.h"
    
    - (UIViewController *) applicationVisibleViewController {
        return [self.window.rootViewController visibleViewController];
    }
    
    0 讨论(0)
  • 2020-12-05 00:48

    If you are using IQKeyboardManager they have an extension in there

    • (UIViewController*)currentViewController;

    so you can do

     application.keyWindow?.currentViewController? // <- there you go
    

    so add this to your pod file

    pod 'IQKeyboardManager'
    
    then pod update and you are away!
    

    hope this helps

    0 讨论(0)
  • 2020-12-05 00:54

    Here's just a quick fix inspired from @krcjr89's answer. The accepted answer doesn't go all the way down the navigation. For instance, if you have a navigation controller embedded in tab bar controller, you won't get to the visible view controller but the navigation controller.

    I made it an extension of UIApplication like @Christian, as this makes the most sense.

    extension UIApplication {
        var visibleViewController: UIViewController? {
            return getVisibleViewController(nil)
        }
    
        private func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
    
            let rootVC = rootViewController ?? UIApplication.shared.keyWindow?.rootViewController
    
            if rootVC!.isKind(of: UINavigationController.self) {
                let navigationController = rootVC as! UINavigationController
                return getVisibleViewController(navigationController.viewControllers.last!)
            }
    
            if rootVC!.isKind(of: UITabBarController.self) {
                let tabBarController = rootVC as! UITabBarController
                return getVisibleViewController(tabBarController.selectedViewController!)
            }
    
            if let presentedVC = rootVC?.presentedViewController {
                return getVisibleViewController(presentedVC)
            }
    
            return rootVC
        }
    }
    
    0 讨论(0)
  • 2020-12-05 00:55

    The top recommendations here will work ok in many scenarios to get the 'best guess' solution but with a few minor adjustments, we can get a more complete solution that doesn't rely on your app's view hierarchy implementation.

    1) Cocoa Touch's view hierarchy allows for multiple children to be present and visible at one time so we need to instead ask for the current visible view controllers (plural) and handle the results accordingly

    2) UINavigationControllers and UITabBarControllers are commonly used in iOS applications, but they are not the only kind of container view controllers. UIKit also supplies the UIPageViewController, UISplitViewController, and allows you to write your own custom container view controllers.

    3) We probably want to ignore popover modals and specific types of view controllers such UIAlertControllers or a custom embedded child-viewcontroller.

    private func visibleViewControllers() -> [UIViewController] {
        guard let root = window?.rootViewController else { return [] }
        return visibleLeaves(from: root, excluding: [UIAlertController.self])
    }
    
    private func visibleLeaves(from parent: UIViewController, excluding excludedTypes: [UIViewController.Type] = []) -> [UIViewController] {
    
        let isExcluded: (UIViewController) -> Bool = { vc in
            excludedTypes.contains(where: { vc.isKind(of: $0) }) || vc.modalPresentationStyle == .popover
        }
    
        if let presented = parent.presentedViewController, !isExcluded(presented) {
            return self.visibleLeaves(from: presented, excluding: excludedTypes)
        }
    
        let visibleChildren = parent.childViewControllers.filter {
            $0.isViewLoaded && $0.view.window != nil
        }
    
        let visibleLeaves = visibleChildren.flatMap {
            return self.visibleLeaves(from: $0, excluding: excludedTypes)
        }
    
        if visibleLeaves.count > 0 {
            return visibleLeaves
        } else if !isExcluded(parent) {
            return [parent]
        } else {
            return []
        }
    }
    
    0 讨论(0)
  • 2020-12-05 00:56

    We implemented it as an UIApplication extension:

    import UIKit
    
    extension UIApplication {
    
        var visibleViewController: UIViewController? {
    
            guard let rootViewController = keyWindow?.rootViewController else {
                return nil
            }
    
            return getVisibleViewController(rootViewController)
        }
    
        private func getVisibleViewController(_ rootViewController: UIViewController) -> UIViewController? {
    
            if let presentedViewController = rootViewController.presentedViewController {
                return getVisibleViewController(presentedViewController)
            }
    
            if let navigationController = rootViewController as? UINavigationController {
                return navigationController.visibleViewController
            }
    
            if let tabBarController = rootViewController as? UITabBarController {
                return tabBarController.selectedViewController
            }
    
            return rootViewController
        }
    }
    
    0 讨论(0)
  • 2020-12-05 00:56

    Here's a Swift 2.3 implementation of @ProgrammierTier's answer as an extension to a UIViewController

    extension UIViewController {
        var visibleViewController: UIViewController? {
            if presentedViewController == nil {
                return self
            }
    
            if let presented = presentedViewController {
                if presented.isKindOfClass(UINavigationController) {
                    let navigationController = presented as! UINavigationController
                    return navigationController.viewControllers.last
                }
    
                if presented.isKindOfClass(UITabBarController) {
                    let tabBarController = presented as! UITabBarController
                    return tabBarController.selectedViewController
                }
    
                return presented.visibleViewController
            }
    
            return nil
        }
    }
    

    To get it from applicationWillResignActive

    func applicationWillResignActive(application: UIApplication) {
        let visibleVC = application.keyWindow?.rootViewController?.visibleViewController
    }
    
    0 讨论(0)
提交回复
热议问题