[ios] iOS에서 최상위 뷰 컨트롤러를 찾는 방법

지금은 “최상위”뷰 컨트롤러 (현재 뷰를 담당하는 컨트롤러)를 찾는 것이 편리한 몇 가지 사례를 겪었지만 그 방법을 찾지 못했습니다.

기본적으로 문제는 다음과 같습니다. 뷰 컨트롤러 (또는 뷰) 가 아닌 (활성 뷰의 주소가없는 ) 클래스에서 실행 중이고 최상위 뷰 컨트롤러의 주소가 전달되지 않은 경우 ( 또는 네비게이션 컨트롤러의 주소), 해당 뷰 컨트롤러를 찾을 수 있습니까? (그렇다면 어떻게?)

아니면 실패하면 최상위 뷰를 찾을 수 있습니까?



답변

iOS 4는 UIWindow에 rootViewController 속성을 도입했습니다.

[UIApplication sharedApplication].keyWindow.rootViewController;

뷰 컨트롤러를 생성 한 후에 직접 설정해야합니다.


답변

허용 된 답변과 @fishstix의 조합이 필요하다고 생각합니다.

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

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

    return topController;
}

스위프트 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
}


답변

JonasG를 완료하려면 답변 (탐색하는 동안 탭 막대 컨트롤러를 생략 한 사람) 현재 보이는 뷰 컨트롤러를 반환하는 내 버전이 있습니다.

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


답변

다른 시나리오를 처리하는 완전한 비 재귀 버전 :

  • 뷰 컨트롤러가 다른 뷰를 제시하고 있습니다
  • 뷰 컨트롤러는 UINavigationController
  • 뷰 컨트롤러는 UITabBarController

목표 -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;
     }
 }

스위프트 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
    }
}


답변

확장 기능을 사용하여 Swift 용 최상위 컨트롤러 얻기

암호:

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

용법:

UIApplication.sharedApplication().keyWindow!.rootViewController!.topMostViewController()


답변

에릭의 답변 을 완성하려면 (팝 오버, 탐색 컨트롤러, tabbarcontrollers, 트래버스 중에 다른 뷰 컨트롤러에 하위 뷰로 추가 된 뷰 컨트롤러 제외) 현재 보이는 뷰 컨트롤러를 반환하는 내 버전이 있습니다.

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

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

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

이제 가장 많은 뷰 컨트롤러를 얻으려면 다음과 같이 위의 메소드를 호출하면됩니다.

UIViewController *topMostViewControllerObj = [self topViewController];


답변

이 답변은 childViewControllers깨끗하고 읽기 쉬운 구현을 포함 하고 유지합니다.

+ (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;
}