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


253

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

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

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


그래서 당신은 그것이 불가능하다는 것을 말하고 있습니다.
핫 릭

@Daniel 아니오, 나는 당신이 이것을 알 필요가 없기 때문에 코드가 일부 재 디자인을 사용할 수있는 것처럼 보입니다. 또한 "최상위"라는 개념은 특정 상황에서만 유효하며 항상 그런 것은 아닙니다.
Dave DeLong

@Daniel 나는 당신의 질문을 잘못 읽었습니다. 이 질문에 대답하려는 많은 if와 buts가 있습니다. 뷰 컨트롤러 흐름에 따라 다릅니다. @Wilbur의 대답은 그것을 추적하기위한 좋은 출발점이되어야합니다.
Deepak Danduprolu

글쎄, 특정 사례로 단순화하자. UIAlertView의 복제본을 작성하려면 어떻게해야합니까? 다른 컨트롤러 나 뷰에 주소 지정을 전달하지 않고도 제대로 작동 할 수 있습니다.
핫 릭

4
@Daniel : 두 번째 UIWindow를 추가하면 경고보기와 같은 오버레이에 효과적입니다.
Wilbur Vandrsmith

답변:


75

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

[UIApplication sharedApplication].keyWindow.rootViewController;

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


155
윌버, 이건 당신이 요청한 것과 반대입니다 rootViewController는 최상위가 아닌 기본보기 컨트롤러입니다.
m4rkk

3
m4rkk : "최고"는보고있는 방향에 따라 다릅니다. 새 컨트롤러가 상단 (스택 형) 또는 하단 (트리 형)에 추가됩니까? 어쨌든, OP는 내비게이션 컨트롤러가 상단에 있다고 언급했으며, 이는 아래로 확대됩니다.
Wilbur Vandrsmith

50
"top"이라는 단어는 뷰 컨트롤러 에 사용되며 맨 위에 표시됩니다 (예 :)-[UINavigationController topViewController] . 그 다음이다 단어 "루트",이 나무의 뿌리 처럼은 ( -[UIWindow rootViewController].
Tricertops

13
@ImpurestClub 설명서 에서 찾을 수 없지만 Xcode가 찾지 못하는 것 같습니다.
Drux

4
@adib 아니오, 그것은 UINavigationController에
David H

428

허용 된 답변과 @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
}

4
또한, 당신은 그것을 확인 UINavigationController하고 요청 topViewController하거나 심지어 확인 UITabBarController하고 요청할 수 selectedViewController있습니다. 그러면 현재 사용자에게 표시되는 뷰 컨트롤러가 표시됩니다.
Tricertops 2016 년

33
childViewController의 계층 구조가 아닌 모달 표시 뷰 컨트롤러의 계층 구조를 순회하기 때문에 불완전한 솔루션입니다 (UINavigationController, UITabBarController 등에서 사용됨).
algal

3
이것은 현재 응용 프로그램 상태로 다시 시작하는 모달보기 컨트롤러의 제시를 추상화하는 좋은 방법입니다. 필자의 경우 응용 프로그램이 시간 초과 된 후 암호 재 입력 화면이었습니다. 감사!
erversteeg

11
@algal : 실제로는 아님 : UITabBarController, UINavigationController 이미 계층 구조에서 최상위 뷰 컨트롤러입니다. "최상위 컨트롤러"로 수행하려는 작업에 따라 해당 컨트롤러 를 전혀 탐색하지 않고 내용을 다루고 싶지 않을 수 있습니다 . 내 경우에는 모든 것 위에 모달 컨트롤러를 제공하는 것이 었 으므로 그 내용이 아닌 UINaviationController 또는 UITabBarController를 가져와야 합니다 !
Rick77

1
@ Rick77, 이것이 사실이라면, 여기에 묻힌 작은 의견은 다른 답변에서 많은 복잡한 수정을 불필요하게 만듭니다. 아무도 이것에 대해 전혀 언급하지 않았기 때문에 나는 그것이 사실인지 확인하도록 요청해야한다고 생각합니다. 그리고 그것이 그렇다면, 그것은 그 자체로 답이 될 가치가있는 것이 중요합니다. 다른 답변의 대부분은이 문제를 해결하기 위해 백 플립을하기 때문에. 당신은 생명을 구할 것입니다!
Le Mot Juiced 2016

150

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

2
니스, 그래 난 TabBar의 컨트롤러 잊어 : P
JonasG

9
포함되지 않음childViewControllers
Awesome-o

팝콘과 같이 @kleo가 생략 한 경우를 처리하여 위의 답변을 개선하는 내 대답을 살펴보십시오. 트래버스를 진행하는 동안 뷰 컨트롤러가 다른 뷰 컨트롤러에 하위 뷰로 추가 된 뷰 컨트롤러
Rajesh

return [self topViewControllerWithRootViewController : navigationController.visibleViewController];를 사용하는 경우 visibleViewController 자체는 UIAlertController 인 경우에도 제시된 뷰 컨트롤러 (IF ANY)를 반환합니다. UI 경보 컨트롤러를 피해야하는 사용자를 위해 visibleViewController 대신 topViewController를 사용하십시오.
Johnykutty

내 50 센트를 추가하기 위해-webView를로드하는 내 뷰 컨트롤러 에서이 작업을 수행하는 데 어려움을 겪고 있습니다. 따라서 보이지 않았습니다. UINavigationController는 아직 보이는 ViewController가없는 동안 보이는 ViewController를 얻으려고 시도했기 때문에 topViewContoller를 얻는 데 실패한 상황이 발생했습니다. 따라서 누군가이 문제에 직면하면 위의 topViewController 메소드를 호출하기 전에보기로드가 완료되었는지 확인하십시오.
mbuster

52

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

  • 뷰 컨트롤러가 다른 뷰를 제시하고 있습니다
  • 뷰 컨트롤러는 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
    }
}

2
나는 그것이 visibleViewController무엇을하는지 명확하게하기 위해 이름을 붙였다 .
Jonny

31

확장 기능을 사용하여 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()

우수-이 솔루션에 대해 대단히 감사합니다. subviews'trick이 필요했습니다! 다시 한번, 많은 감사, 당신은 내 하루를 구했다.
iKK

25

에릭의 답변 을 완성하려면 (팝 오버, 탐색 컨트롤러, 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];

SplitViewController도 없습니까?
apinho

21

이 답변은 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;
}

코드를 최소화하고 다시 복원하여 어떤 컨트롤러인지 표시하기 때문에 일부 코드가 업데이트되었습니다. nik-kov-ios-developer.blogspot.ru/2016/12/…
Nik Kov

"topVisibleViewController"는 어디에 있습니까?
파라다이스

12

나는 최근 에이 상황을 내 프로젝트 중 하나로 보았습니다. 네트워크 상태가 변경되었을 때 컨트롤러가 표시되는 유형과 유형 (UINavigationController, 클래식 컨트롤러 또는 사용자 정의보기 컨트롤러)이 무엇이든 알림보기를 표시해야했습니다.

그래서 코드를 공개했습니다.이 프로토콜은 매우 쉽고 실제로 프로토콜을 기반으로하므로 모든 유형의 컨테이너 컨트롤러에 유연합니다. 마지막 답변과 관련이 있지만 훨씬 유연한 방식으로 보입니다.

여기에서 코드를 얻을 수 있습니다 : PPTopMostController

그리고 가장 많은 컨트롤러를 사용하여

UIViewController *c = [UIViewController topMostController];

10

이것은 Eric의 답변을 개선 한 것입니다.

UIViewController *_topMostController(UIViewController *cont) {
    UIViewController *topController = cont;

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

    if ([topController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visible = ((UINavigationController *)topController).visibleViewController;
        if (visible) {
            topController = visible;
        }
    }

    return (topController != cont ? topController : nil);
}

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

    UIViewController *next = nil;

    while ((next = _topMostController(topController)) != nil) {
        topController = next;
    }

    return topController;
}

_topMostController(UIViewController *cont) 도우미 기능입니다.

이제 당신이해야 할 일은 호출 topMostController()이며 가장 많은 UIViewController가 반환되어야합니다!


7
1983 년부터 나는 말할 것이다. Objective-C에 C가 포함되어 있음을 기억하십시오. ObjC 코드를 C 함수로 랩핑하는 것이 일반적인 관행이므로 예, Objective-C 코드입니다.
JonasG

@JonasG 안녕하세요 Jonas, 어떤 상황에서 ObjC 코드를 C로 래핑하는 것을 선호합니까? 때로는 C와 같은 C 함수가 표시되기 때문에 사용법을 구별 할 수 없습니다. C로 코드를 래핑하면 성능상의 이점이 있습니까?
OzBoz

1
@OzBoz 어떤 클래스 self가 속해야하는지 명확하지 않은 상황에서 .
adib

8

여기에 내가 걸릴 것입니다. UIAlertView를 최상위 컨트롤러로 사용하는 것을 건너 뛰는 방법을 지적 한 @Stakenborg에게 감사드립니다.

-(UIWindow *) returnWindowWithWindowLevelNormal
{
    NSArray *windows = [UIApplication sharedApplication].windows;
    for(UIWindow *topWindow in windows)
    {
        if (topWindow.windowLevel == UIWindowLevelNormal)
            return topWindow;
    }
    return [UIApplication sharedApplication].keyWindow;
}

-(UIViewController *) getTopMostController
{
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal)
    {
        topWindow = [self returnWindowWithWindowLevelNormal];
    }

    UIViewController *topController = topWindow.rootViewController;
    if(topController == nil)
    {
        topWindow = [UIApplication sharedApplication].delegate.window;
        if (topWindow.windowLevel != UIWindowLevelNormal)
        {
            topWindow = [self returnWindowWithWindowLevelNormal];
        }
        topController = topWindow.rootViewController;
    }

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

    if([topController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *nav = (UINavigationController*)topController;
        topController = [nav.viewControllers lastObject];

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

    return topController;
}

getSomething:Objective-C에서 와 같이 이름 지정 방법을 피해야 합니다. 이것은 특별한 의미를 지니고 있으며 (more : cocoadevcentral.com/articles/000082.php ) 코드에서 이러한 요구 사항을 충족하지 않습니다.
Vive

7
@implementation UIWindow (확장자)

-(UIViewController *) topMostController
{
    UIViewController * topController = [자체 rootViewController];

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

    return topController;
}

@종료

원래 게시물에 명시된 조건을 만족하지 않았다고 생각합니다.
핫 릭

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

나는 이것을 사용했지만, 하나 이상의 제시된 뷰 컨트롤러가있을 때 중단된다는 것을 주목하라
Chuck Boris

7

최신 Swift 버전 :
파일을 작성하고 이름을 지정한 UIWindowExtension.swift후 다음 스 니펫을 붙여 넣으십시오.

import UIKit

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

    public static func getVisibleViewControllerFrom(vc: UIViewController?) -> UIViewController? {
        if let nc = vc as? UINavigationController {
            return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
        } else if let tc = vc as? UITabBarController {
            return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
        } else {
            if let pvc = vc?.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(pvc)
            } else {
                return vc
            }
        }
    }
}

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

다음과 같이 어디서나 사용하십시오 :

if let topVC = getTopViewController() {

}

나는 당신의 대답을 너무 많이 바꾸고 싶지 않지만 몇 가지 제안 할 것입니다. 1. UISplitViewController에 대한 지원을 추가하십시오. 2. switch다른 경우 대신 사용하십시오 . 3. 정적 함수가 필요한지 확실하지 않습니다. 선언 한 첫 번째 인스턴스 수준 var에서 쉽게 수행 할 수 있다고 생각합니다. 4. 너무 많은 전역 함수를 생성하지 않는 것이 가장 좋지만 맛의 문제입니다. 한 줄의 코드를 사용하여 전역 함수의 효과를 얻을 수 있습니다.UIApplication.sharedApplication().delegate?.window?.visibleViewController
Jordan Smith

7

간단한 확장 UIApplication스위프트에서의 :

노트:

그것은 moreNavigationController내 관심UITabBarController

extension UIApplication {

    class func topViewController(baseViewController: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {

        if let navigationController = baseViewController as? UINavigationController {
            return topViewController(navigationController.visibleViewController)
        }

        if let tabBarViewController = baseViewController as? UITabBarController {

            let moreNavigationController = tabBarViewController.moreNavigationController

            if let topViewController = moreNavigationController.topViewController where topViewController.view.window != nil {
                return topViewController(topViewController)
            } else if let selectedViewController = tabBarViewController.selectedViewController {
                return topViewController(selectedViewController)
            }
        }

        if let splitViewController = baseViewController as? UISplitViewController where splitViewController.viewControllers.count == 1 {
            return topViewController(splitViewController.viewControllers[0])
        }

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

        return baseViewController
    }
}

간단한 사용법 :

if let topViewController = UIApplication.topViewController() {
    //do sth with top view controller
}

예 예 예-웹에는 topMostViewController를 찾는 많은 솔루션이 있지만 앱에 더 많은 탭이있는 탭 막대가있는 경우 조금 다르게 처리해야합니다.
Andy Obusek

7

현재 보이는 부분을 잡으려면 아래 확장명을 사용하십시오 UIViewController. Swift 4.0 이상에서 일했습니다.

스위프트 4.0 이상 :

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

사용하는 방법?

let objViewcontroller = UIApplication.topViewController()

해야하지 않는이 테스트 presentedViewController전과 첫째, UINavigationControllerUITabBarController경우? 그렇지 않으면 뷰 컨트롤러가 UINavigationController또는 에서 모달로 표시되는 경우 표시되는 UITabBarController뷰 컨트롤러 인 경우에도 최상위 뷰 컨트롤러로 반환되지 않습니다.
Drew

4

또 다른 스위프트 솔루션

func topController() -> UIViewController? {

    // recursive follow
    func follow(from:UIViewController?) -> UIViewController? {
        if let to = (from as? UITabBarController)?.selectedViewController {
            return follow(to)
        } else if let to = (from as? UINavigationController)?.visibleViewController {
            return follow(to)
        } else if let to = from?.presentedViewController {
            return follow(to)
        }
        return from
    }

    let root = UIApplication.sharedApplication().keyWindow?.rootViewController

    return follow(root)

}

4

스위프트 4.2 확장


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

어디서나 사용하세요

 UIApplication.topViewController()?.present(yourController, animated: true, completion: nil)

또는

 UIApplication.topViewController()?
                    .navigationController?
                    .popToViewController(yourController,
                                         animated: true)

UINavigationController, UITabBarController와 같은 클래스에 적합

즐겨!


1
@BilalBakhrom은 "공감했습니다. 귀하의 답변이 최고라고 생각합니다. 직접 topViewController () 메소드를 호출 할 수 없습니다. UIApplication 클래스는 싱글 톤입니다."shared "라는 인스턴스를 사용하십시오." 내가 거부하기로 한 편집 에서 이것이 실제로 올바른 경우 여기를 클릭하여 해당 편집을 승인 할 수있는 메뉴로 이동하십시오.
wizzwizz4

3

여기 나를 위해 일한 것이 있습니다.

keyWindow는 경고와 같은 일부 OS이므로 컨트롤러가 키 창에서 때때로 없음을 발견했습니다.

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

     if (topController == nil)
     {
         // The windows in the array are ordered from back to front by window level; thus,
         // the last window in the array is on top of all other app windows.
         for (UIWindow *aWndow in [[UIApplication sharedApplication].windows reverseObjectEnumerator])
         {
             topController = aWndow.rootViewController;
             if (topController)
                 break;
         }
     }

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

     return topController;
 }

3

@Eric의 답변을 확장하면 keyWindow가 실제로 원하는 창인지주의해야합니다. 예를 들어 경고보기에서 무언가를 누른 후이 방법을 사용하려는 경우 keyWindow는 실제로 경고의 창이며 의심 할 여지없이 문제를 일으킬 수 있습니다. 경고를 통해 딥 링크를 처리하고 스택 추적이없는 SIGABRT를 발생시킬 때이 문제가 발생했습니다. 디버깅 할 총 암캐

지금 사용하고있는 코드는 다음과 같습니다.

- (UIViewController *)getTopMostViewController {
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal) {
        NSArray *windows = [UIApplication sharedApplication].windows;
        for(topWindow in windows)
        {
            if (topWindow.windowLevel == UIWindowLevelNormal)
                break;
        }
    }

    UIViewController *topViewController = topWindow.rootViewController;

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

    return topViewController;
}

이 질문에 대한 다른 답변에서 원하는 상위 뷰 컨트롤러를 검색하는 취향과 자유롭게 혼합하십시오.


이것이 완벽한 솔루션이라는 것을 알았습니까? 다른 많은 답변은 매우 복잡하여 많은 경우를 설명하려고합니다. 나는 이것이 사실이기를 원합니다 . 너무 간단하고 우아합니다.
Le Mot Juiced 2016

나는 그것에 문제가 없었습니다. 탐색 스택에서 비정상적인 작업을 수행하지 않으면 이것이 작동합니다. 그렇지 않으면 다른 솔루션 중 일부가 더 복잡한 경우를 처리합니다.
Stakenborg

3

대체 스위프트 솔루션 :

static func topMostController() -> UIViewController {
    var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
    while (topController?.presentedViewController != nil) {
        topController = topController?.presentedViewController
    }

    return topController!
}

3

이 솔루션이 가장 완벽합니다. UINavigationController UIPageViewController UITabBarController 및 상위 뷰 컨트롤러에서 가장 많이 표시되는 뷰 컨트롤러

예는 Swift 3에 있습니다.

과부하가 3 개 있습니다

//Get the topmost view controller for the current application.
public func MGGetTopMostViewController() -> UIViewController?  {

    if let currentWindow:UIWindow = UIApplication.shared.keyWindow {
        return MGGetTopMostViewController(fromWindow: currentWindow)
    }

    return nil
}

//Gets the topmost view controller from a specific window.
public func MGGetTopMostViewController(fromWindow window:UIWindow) -> UIViewController? {

    if let rootViewController:UIViewController = window.rootViewController
    {
        return MGGetTopMostViewController(fromViewController:  rootViewController)
    }

    return nil
}


//Gets the topmost view controller starting from a specific UIViewController
//Pass the rootViewController into this to get the apps top most view controller
public func MGGetTopMostViewController(fromViewController viewController:UIViewController) -> UIViewController {

    //UINavigationController
    if let navigationViewController:UINavigationController = viewController as? UINavigationController {
        let viewControllers:[UIViewController] = navigationViewController.viewControllers
        if navigationViewController.viewControllers.count >= 1 {
            return MGGetTopMostViewController(fromViewController: viewControllers[viewControllers.count - 1])
        }
    }

    //UIPageViewController
    if let pageViewController:UIPageViewController = viewController as? UIPageViewController {
        if let viewControllers:[UIViewController] = pageViewController.viewControllers {
            if viewControllers.count >= 1 {
                return MGGetTopMostViewController(fromViewController: viewControllers[0])
            }
        }
    }

    //UITabViewController
    if let tabBarController:UITabBarController = viewController as? UITabBarController {
        if let selectedViewController:UIViewController = tabBarController.selectedViewController {
            return MGGetTopMostViewController(fromViewController: selectedViewController)
        }
    }

    //Lastly, Attempt to get the topmost presented view controller
    var presentedViewController:UIViewController! = viewController.presentedViewController
    var nextPresentedViewController:UIViewController! = presentedViewController?.presentedViewController

    //If there is a presented view controller, get the top most prensentedViewController and return it.
    if presentedViewController != nil {
        while nextPresentedViewController != nil {

            //Set the presented view controller as the next one.
            presentedViewController = nextPresentedViewController

            //Attempt to get the next presented view controller
            nextPresentedViewController = presentedViewController.presentedViewController
        }
        return presentedViewController
    }

    //If there is no topmost presented view controller, return the view controller itself.
    return viewController
}

3

Swift 4.2의 간결하면서도 포괄적 인 솔루션은 UINavigationControllers , UITabBarControllers , 제시자식 보기 컨트롤러를 고려합니다.

extension UIViewController {
  func topmostViewController() -> UIViewController {
    if let navigationVC = self as? UINavigationController,
      let topVC = navigationVC.topViewController {
      return topVC.topmostViewController()
    }
    if let tabBarVC = self as? UITabBarController,
      let selectedVC = tabBarVC.selectedViewController {
      return selectedVC.topmostViewController()
    }
    if let presentedVC = presentedViewController {
      return presentedVC.topmostViewController()
    }
    if let childVC = children.last {
      return childVC.topmostViewController()
    }
    return self
  }
}

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

용법:

let viewController = UIApplication.shared.topmostViewController()

2

Swift의 훌륭한 솔루션, AppDelegate에서 구현

func getTopViewController()->UIViewController{
    return topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
}
func topViewControllerWithRootViewController(rootViewController:UIViewController)->UIViewController{
    if rootViewController is UITabBarController{
        let tabBarController = rootViewController as! UITabBarController
        return topViewControllerWithRootViewController(tabBarController.selectedViewController!)
    }
    if rootViewController is UINavigationController{
        let navBarController = rootViewController as! UINavigationController
        return topViewControllerWithRootViewController(navBarController.visibleViewController)
    }
    if let presentedViewController = rootViewController.presentedViewController {
        return topViewControllerWithRootViewController(presentedViewController)
    }
    return rootViewController
}

1

나는 대부분의 답변이 완전히 무시되었다고 생각 UINavigationViewController하므로 다음 구현 으로이 사용 사례를 처리했습니다.

+ (UIViewController *)topMostController {
    UIViewController * topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController || [topController isMemberOfClass:[UINavigationController class]]) {
        if([topController isMemberOfClass:[UINavigationController class]]) {
            topController = [topController childViewControllers].lastObject;
        } else {
            topController = topController.presentedViewController;
        }
    }

    return topController;
}

1

나는 매우 늦었고 중복 될 수 있음을 알고 있습니다. 그러나 다음은 내가 생각해 낸 스 니펫입니다.

    static func topViewController() -> UIViewController? {
        return topViewController(vc: UIApplication.shared.keyWindow?.rootViewController)
    }

    private static func topViewController(vc:UIViewController?) -> UIViewController? {
        if let rootVC = vc {
            guard let presentedVC = rootVC.presentedViewController else {
                return rootVC
            }
            if let presentedNavVC = presentedVC as? UINavigationController {
                let lastVC = presentedNavVC.viewControllers.last
                return topViewController(vc: lastVC)
            }
            return topViewController(vc: presentedVC)
        }
        return nil
    }

0

이것은 모든 루트보기 제어에서 최상위 viewController 1 을 찾는 데 효과적입니다.

+ (UIViewController *)topViewControllerFor:(UIViewController *)viewController
{
    if(!viewController.presentedViewController)
        return viewController;
    return [MF5AppDelegate topViewControllerFor:viewController.presentedViewController];
}

/* View Controller for Visible View */

AppDelegate *app = [UIApplication sharedApplication].delegate;
UIViewController *visibleViewController = [AppDelegate topViewControllerFor:app.window.rootViewController]; 

0

이것이 최상위 뷰 컨트롤러를 찾아서 달성하려는 작업에 도움이되는지 확실하지 않지만 새로운 뷰 컨트롤러를 제시하려고했지만 루트 뷰 컨트롤러에 이미 모달 대화 상자가 있으면 차단되어 있으므로 이 코드를 사용하여 모든 모달보기 컨트롤러의 상단으로 순환합니다.

UIViewController* parentController =[UIApplication sharedApplication].keyWindow.rootViewController;

while( parentController.presentedViewController &&
       parentController != parentController.presentedViewController )
{
    parentController = parentController.presentedViewController;
}

0

다음을 사용하여 가장 많은 뷰 컨트롤러를 찾을 수 있습니다

NSArray *arrViewControllers=[[self navigationController] viewControllers];
UIViewController *topMostViewController=(UIViewController *)[arrViewControllers objectAtIndex:[arrViewControllers count]-1];

self외에는 실제로 질문을 읽으면 아무런 navigationController속성 이 없습니다 .
핫 릭

0

또 다른 솔루션은 첫 번째 응답자가 무엇인지에 따라 작동하거나 작동하지 않을 수있는 응답자 체인에 의존합니다.

  1. 첫 번째 응답자를 얻으십시오 .
  2. 해당 첫 번째 응답자와 연관된 UIViewController를 가져 오십시오 .

의사 코드 예 :

+ (UIViewController *)currentViewController {
    UIView *firstResponder = [self firstResponder]; // from the first link above, but not guaranteed to return a UIView, so this should be handled more appropriately.
    UIViewController *viewController = [firstResponder viewController]; // from the second link above
    return viewController;
}

0

빠른:

extension UIWindow {

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

class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
if vc.isKindOfClass(UINavigationController.self) {

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

} else if vc.isKindOfClass(UITabBarController.self) {

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

} else {

    if let presentedViewController = vc.presentedViewController {

        return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)

    } else {

        return vc;
    }
}
}

용법:

 if let topController = window.visibleViewController() {
            println(topController)
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.