가장 많이 UIViewController 받기


192

UIViewController액세스하지 않으면 최고를 얻을 수없는 것 같습니다 UINavigationController. 여기 내가 지금까지 가지고있는 것입니다 :

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(vc, animated: true, completion: nil)

그러나 아무것도하지 않는 것 같습니다. keyWindowrootViewController옵션 체인은 문제가되지해야한다, 그래서 너무 nil이 아닌 값이 될 것으로 보인다.

참고 : 이와 같은 작업을 수행하는 것은 좋지 않습니다. MVC 패턴을 깨뜨립니다.


여기에 하나 개의 대안 솔루션을 사용할 수 있습니다 stackoverflow.com/a/39994115/1872233은
iDevAmit

답변:


287

presentViewController뷰 컨트롤러를 보여줍니다. 뷰 컨트롤러를 반환하지 않습니다. 를 사용하지 않는 경우 UINavigationController아마도 찾고 presentedViewController있을 것이고 루트에서 시작하여 제시 된보기를 반복해야합니다.

if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}

스위프트 3 이상 :

if var topController = UIApplication.shared.keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}

iOS 13 이상

let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

if var topController = keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

// topController should now be your topmost view controller
}

1
누군가 while 루프를 설명 할 수 있습니까? 나에게 그것은 반복 할 것이없는 것처럼 보인다. 왜 이것이 컴파일되는지 확실하지 않습니다.
Tom Tom

15
@ProfessorTom 루프 topController.presentedViewController는 무언가 를 반환하는 한 계속됩니다 (즉, 컨트롤러에는 자식 컨트롤러가 있습니다). 그건 while let사실 적용 할 topController.presentedViewController뭔가를 반환해야합니다. nil을 반환하면 (즉, 컨트롤러에 자식이없는 경우) 루핑이 중지됩니다. 루프의 본문에서 자식을 current로 다시 할당하고 topController다시 반복하여 뷰 컨트롤러 계층 구조로 내려갑니다. 외부 명령문 에있는 topController것처럼 다시 할당 할 수 있습니다 . varif
rickerbh

1
감사합니다. 의 온라인 예를 찾을 수 없습니다 while let. 물론 if let찾아야 할 많은 예가 있습니다.
Tom Tom

1
let x = somethingThatCouldBeNil구문은 실제 값 / 조건이 사용될 수있는 모든 곳에서 사용하기에 매우 유용한 트릭입니다. 여기서 사용하지 않았다면 명시 적으로 값을 할당 한 다음 실제로 값이 있는지 테스트해야합니다. 나는 그것이 간결하고 표현 적이라고 생각합니다.
rickerbh

1
나는 그 트릭에 익숙하다. while 루프에 대해 추론하기가 좀 더 어렵다.
Tom

272

이 확장명을 가지고

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

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

컨트롤러 어디에서나 사용할 수 있습니다

if let topController = UIApplication.topViewController() {

}

1
확장 팁 주셔서 감사합니다 :)
Thein

4
이 답변에 대한 중요한 편집을 시도했지만 거부되었습니다 (왜 그런지 이유와 템플릿 이유가 이해가되지 않습니다) : 재귀에 사용하기 전에 nav.visibleViewController가 nil인지 확인하는 것이 중요합니다 그렇지 않은 경우 재귀 무한 루프에 빠지기 때문에 (tab.selectedViewController가 확인되는 방식과 마찬가지로) 호출하십시오.
Ethan G

@EthanG 내 이해에 따르면 nav.visibleViewController가 nil이면 함수는 nil을 반환합니다 (마지막으로 떨어짐 return). 어떻게 무한 루프에 들어갈 수 있습니까?
Desmond DAI

3
나는의 UIViewController의 정적 함수로이를 만들기 위해 더 논리적 일 것 같아
Leszek이 Zarna에게

1
UITabBarControllers에서 모달 표시 뷰 컨트롤러를 잡으려면 'presentedViewController'체크가 먼저 나올 것입니다.
Tokuriku

65

Swift 4/5 +의 최상위 뷰를 얻으려면

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

사용하는 방법

if let topVC = UIApplication.getTopViewController() {
   topVC.view.addSubview(forgotPwdView)
}

2
훌륭한 솔루션. 감사!
Andrey M.

2
'keyWindow'는 iOS 13.0에서 더 이상 사용되지 않습니다.
rs7

2
'keyWindow'는 iOS 13.0에서 더 이상 사용되지 않습니다. stackoverflow.com/a/57899013/4514671
Rebeloper

19
extension UIWindow {

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

    static func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
        if let navigationController = vc as? UINavigationController,
            let visibleController = navigationController.visibleViewController  {
            return UIWindow.getVisibleViewControllerFrom( vc: visibleController )
        } else if let tabBarController = vc as? UITabBarController,
            let selectedTabController = tabBarController.selectedViewController {
            return UIWindow.getVisibleViewControllerFrom(vc: selectedTabController )
        } else {
            if let presentedViewController = vc.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController)
            } else {
                return vc
            }
        }
    }
}

용법:

if let topController = window.visibleViewController() {
    println(topController)
}

이 솔루션은 정말 유망 해 보였지만, 푸시 알림을받을 때 사용중인 뷰 컨트롤러를 가져 오려고이 명령을 실행하려고했는데return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)
Mike

@ 마찬가지로 presentViewController가 아닌 presentViewController 만 사용해야합니다. presentedViewController
앨 라이

@allaire 모달 뷰 컨트롤러 위에 모달 뷰 컨트롤러를 표시했다면 필요 .presentedViewController.presentedViewController합니까?
Baran Emre

6

Objective-C 버전 인 Dianz 답변을 기반으로 함

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

UITabBarController의 UINavigationController에서 작동하지 않습니다. UINavigationController를 반환하고 탐색이 중단 된 상태에서 topController를 반환해야합니다.
Mike.R

Tnx Tnx Tnx Bro
reza_khalafi

6

나는 @ dianz 's answer을 좋아했으며 여기에 신속한 3 버전이 있습니다. 기본적으로는 동일하지만 중괄호가 누락되어 구문 / 변수 / 방법 이름 중 일부가 변경되었습니다. 그래서 여기 있습니다!

extension UIApplication {
    class func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = base as? UINavigationController {
            return topViewController(base: nav.visibleViewController)
        }
        if let tab = base as? UITabBarController {
            if let selected = tab.selectedViewController {
                return topViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topViewController(base: presented)
        }
        return base
    }
}

사용법은 여전히 ​​똑같습니다.

if let topController = UIApplication.topViewController() {
    print("The view controller you're looking at is: \(topController)")
}

6

https://gist.github.com/db0company/369bfa43cb84b145dfd8 이 사이트의 답변과 의견에 대해 몇 가지 테스트를 수행했습니다. 나를 위해 다음과 같은 작품

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

그런 다음 상단 뷰 컨트롤러를 가져옵니다.

UIApplication.shared.topMostViewController()

5

이 코드를 사용하여 최상위 UIViewController를 찾으십시오.

func getTopViewController() -> UIViewController? {
    var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
    while topController?.presentedViewController != nil {
        topController = topController?.presentedViewController
    }
    return topController
}

2
이것은 rickerbh의 답변과 어떻게 다릅니 까?
ElectroBuddha

5

함수 대신 계산 된 변수를 사용하여 @AlberZou에서 약간의 변형

extension UIViewController {
  var 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 {
  var topMostViewController : UIViewController? {
    return self.keyWindow?.rootViewController?.topMostViewController
  }
}

그럼 말해

if let topViewControler = UIApplication.shared.topMostViewController {
    ... do stuff
}

4

위의 Bob -c를 ​​기반으로 :

스위프트 3.0

extension UIWindow {


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

    class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {

        if vc.isKind(of: UINavigationController.self) {

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

        } else if vc.isKind(of: UITabBarController.self) {

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

        } else {

            if let presentedViewController = vc.presentedViewController {

                return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController)

            } else {

                return vc;
            }
        }
    }
}

4

풍미가 너무 많지만 반복적 인 정교한 맛은 없습니다. 이전 것들과 결합 :

     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
    }

2

AppDelegate에서 UIViewController 변수를 정의 할 수 있으며 모든 viewWillAppear에서 변수를 self로 설정합니다 (단, dianz 응답이 가장 적합합니다).

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
    appDel.currentVC = self
}

1
내비게이션 컨트롤을 얻으려고 할 때 다른 솔루션으로 저에게 잘 작동합니다. nil을 반환하므로 새로운 vc를 푸시 할 수 없었습니다.
Amr Angry

currentVC가 약한 참조로 정의되어 있는지 확인하십시오. 그렇지 않으면 메모리 누수가 발생합니다.
bubuxu

2

Swift 3에서 보이는 viewController를 찾으려면

if let viewControllers = window?.rootViewController?.childViewControllers {

     let prefs = UserDefaults.standard

     if viewControllers[viewControllers.count - 1] is ABCController{
        print("[ABCController] is visible")

     }
}

이 코드는 마지막으로 추가되었거나 마지막으로 활성화 된 컨트롤러를 찾습니다.

이것은 AppDelegate에서 활성 뷰 컨트롤러를 찾기 위해 사용했습니다.


2
import UIKit

extension UIApplication {

    // MARK: Choose keyWindow as per your choice
    var currentWindow: UIWindow? {
        connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
    }

    // MARK: Choose keyWindow as per your choice
    var keyWindow: UIWindow? {
        UIApplication.shared.windows.first { $0.isKeyWindow }
    }

    class func topMostViewController(base: UIViewController? = UIApplication.shared.currentWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return topMostViewController(base: nav.visibleViewController)
        }

        if let tab = base as? UITabBarController {
            let moreNavigationController = tab.moreNavigationController

            if let top = moreNavigationController.topViewController, top.view.window != nil {
                return topMostViewController(base: top)
            } else if let selected = tab.selectedViewController {
                return topMostViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topMostViewController(base: presented)
        }
        return base
    }
}

'visibleViewController'의 모호한 사용
Omar N Shamali

1

코드를 어디에 넣었습니까?

데모에서 코드를 시험해 보았습니다. 코드를 넣으면 알게되었습니다.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 

키 창이 아직 설정되었으므로 실패합니다.

하지만 코드를 뷰 컨트롤러에 넣습니다.

override func viewDidLoad() {

그냥 작동합니다.


에 없습니다 didFinishLaunchingWithOptions. 다양한 디버그 목적 으로이 기능이 필요합니다.
Zoyt

1

매우 드문 경우이지만 사용자 정의 segue를 사용하면 최상위 뷰 컨트롤러가 탐색 스택 또는 탭 막대 컨트롤러에 있지 않거나 표시되지만 뷰가 키 윈도우의 하위 뷰 상단에 삽입됩니다.

이러한 상황에서는 UIApplication.shared.keyWindow.subviews.last == self.view현재 뷰 컨트롤러가 최상위 컨트롤러인지 확인해야합니다.


1

신속한 5 / iOS 13+ 솔루션을 찾고있는 사람 keywindow은 ( 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
        }
    }
}

어떻게 사용합니까?
Chris Comas

그냥 이렇게 불러 UIApplication.getTopMostViewController()ViewController 내부에 @ChrisComas
Virendra

0
  var topViewController: UIViewController? {
        guard var topViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
        while let presentedViewController = topViewController.presentedViewController {
            topViewController = presentedViewController
        }
        return topViewController
    }

0

나에게 가장 좋은 해결책은 기능이있는 확장입니다. 이 확장명을 가진 신속한 파일을 만드십시오

먼저 UIWindow 확장입니다 .

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

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

그 파일 안에 함수 추가

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

그리고 그것을 사용하고 싶다면 어디서나 전화 할 수 있습니다. :

  override func viewDidLoad() {
    super.viewDidLoad()
      if let topVC = visibleViewController() {
             //show some label or text field 
    }
}

파일 코드는 다음과 같습니다 .

import UIKit

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

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

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