Получить топ самых UIViewController
Я не могу получить самый верх UIViewController
без доступа к UINavigationController
, Вот что у меня так далеко:
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(vc, animated: true, completion: nil)
Тем не менее, он, похоже, ничего не делает. keyWindow
а также rootViewController
кажется, что значения не равны нулю, поэтому необязательное сцепление не должно быть проблемой.
Заранее спасибо!
ПРИМЕЧАНИЕ. Плохо делать что-то подобное. Это нарушает схему MVC.
29 ответов
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
}
Для Swift 3+:
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
// topController should now be your topmost view 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
}
}
Свифт 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() {
}
Для swift4 найти самый верхний viewController
extension UIApplication {
class func getTopMostViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return getTopMostViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return getTopMostViewController(base: selected)
}
}
if let presented = base?.presentedViewController {
return getTopMostViewController(base: presented)
}
return base
}
}
Как пользоваться
if let topVC = UIApplication.getTopMostViewController() {
topVC.view.addSubview(forgotPwdView)
}
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)
}
Я использую следующий код, чтобы получить topViewController-
Он обратно совместим со старыми версиями iOS, а также поддерживает UIScene.
extension UIApplication {
func topViewController() -> UIViewController? {
var topViewController: UIViewController? = nil
if #available(iOS 13, *) {
for scene in connectedScenes {
if let windowScene = scene as? UIWindowScene {
for window in windowScene.windows {
if window.isKeyWindow {
topViewController = window.rootViewController
}
}
}
}
} else {
topViewController = keyWindow?.rootViewController
}
while true {
if let presented = topViewController?.presentedViewController {
topViewController = presented
} else if let navController = topViewController as? UINavigationController {
topViewController = navController.topViewController
} else if let tabBarController = topViewController as? UITabBarController {
topViewController = tabBarController.selectedViewController
} else {
// Handle any other third party container in `else if` if required
break
}
}
return topViewController
}
}
Его можно было использовать так:
let topController = UIApplication.shared.topViewController()
topController?.present(controllerToPresent, animated: true, completion: nil)
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
}
}
iOS13+ // самый верхний контроллер просмотра
extension UIViewController {
func topMostViewController() -> UIViewController {
if self.presentedViewController == nil {
return self
}
if let navigation = self.presentedViewController as? UINavigationController {
return navigation.visibleViewController!.topMostViewController()
}
if let tab = self.presentedViewController as? UITabBarController {
if let selectedTab = tab.selectedViewController {
return selectedTab.topMostViewController()
}
return tab.topMostViewController()
}
return self.presentedViewController!.topMostViewController()
}
}
extension UIApplication {
func topMostViewController() -> UIViewController? {
return UIWindow.key!.rootViewController?.topMostViewController()
}
}
extension UIWindow {
static var key: UIWindow? {
if #available(iOS 13, *) {
return UIApplication.shared.windows.first { $0.isKeyWindow }
} else {
return UIApplication.shared.keyWindow
}
}
}
//use let vc = UIApplication.shared.topMostViewController()
// End top Most view Controller
Мне понравился ответ @dianz, и вот его версия Swift 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)")
}
Используйте этот код, чтобы найти самый верхний UIViewController
func getTopViewController() -> UIViewController? {
var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
while topController?.presentedViewController != nil {
topController = topController?.presentedViewController
}
return topController
}
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()
}
}
Затем получите верхний viewController по:
UIApplication.shared.topMostViewController()
Для Swift 5+,iOS 13+
extension UIViewController {
static func topMostViewController() -> UIViewController? {
if #available(iOS 13.0, *) {
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
return keyWindow?.rootViewController?.topMostViewController()
}
return UIApplication.shared.keyWindow?.rootViewController?.topMostViewController()
}
func topMostViewController() -> UIViewController? {
if let navigationController = self as? UINavigationController {
return navigationController.topViewController?.topMostViewController()
}
else if let tabBarController = self as? UITabBarController {
if let selectedViewController = tabBarController.selectedViewController {
return selectedViewController.topMostViewController()
}
return tabBarController.topMostViewController()
}
else if let presentedViewController = self.presentedViewController {
return presentedViewController.topMostViewController()
}
else {
return self
}
}
}
Применение:
Когда вы получаете topMostViewController без экземпляра UIViewController
guard let viewController = UIViewController.topMostViewController() else { return }
print(viewController)
Когда вы получаете topMostViewController экземпляра UIViewController
let yourVC = UIViewController()
guard let vc = yourVC.topMostViewController() else { return }
print(vc)
Основываясь на ответе Dianz, версия Objective-C
- (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;
}
В SWIFT 5.2
вы можете использовать под кодом:
import UIKit
extension UIWindow {
static func getTopViewController() -> UIViewController? {
if #available(iOS 13, *){
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if var topController = keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
} else {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
}
return nil
}
}
Небольшое изменение @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
}
Swift 5+
Объявить
func topViewController() -> 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
}
}
Использовать
topViewController()?.view.addSubview(UIView())
На основании Боба -c выше:
Swift 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;
}
}
}
}
Слишком много ароматов, но нет итеративно разработанного. В сочетании с предыдущими:
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
}
Вы можете определить переменную UIViewController в AppDelegate, и в каждом viewWillAppear установите для переменной значение self (однако dianz-ответ - лучший ответ).
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
appDel.currentVC = self
}
extension UIApplication {
public var mainKeyWindow: UIWindow? {
return windows.first(where: { $0.isKeyWindow }) ?? keyWindow
}
public var rootViewController: UIViewController? {
guard let keyWindow = UIApplication.shared.mainKeyWindow, let rootViewController = keyWindow.rootViewController else {
return nil
}
return rootViewController
}
public func topViewController(controller: UIViewController? = UIApplication.shared.rootViewController) -> UIViewController? {
if controller == nil {
return topViewController(controller: rootViewController)
}
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selectedViewController = tabController.selectedViewController {
return topViewController(controller: selectedViewController)
}
}
if let presentedViewController = controller?.presentedViewController {
return topViewController(controller: presentedViewController)
}
return controller
}
}
Swift 5.2 и выше
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
}
Чтобы найти видимый viewController в Swift 3
if let viewControllers = window?.rootViewController?.childViewControllers {
let prefs = UserDefaults.standard
if viewControllers[viewControllers.count - 1] is ABCController{
print("[ABCController] is visible")
}
}
Этот код находит последний добавленный или последний видимый активный контроллер.
Это я использовал в AppDelegate, чтобы найти активный контроллер представления
В очень редком случае, с пользовательским переходом, самый верхний контроллер представления не находится в стеке навигации или контроллере панели вкладок или не представлен, но его представление вставляется в верхнюю часть подпредставлений ключевых ветров.
В такой ситуации необходимо проверить, UIApplication.shared.keyWindow.subviews.last == self.view
определить, является ли текущий контроллер представления самым верхним.
попробуй это
let topVisibleVC = UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
Для тех, кто ищет быстрое решение 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
}
}
}
var topViewController: UIViewController? {
guard var topViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
while let presentedViewController = topViewController.presentedViewController {
topViewController = presentedViewController
}
return topViewController
}
Где вы положили код?
Я попробую ваш код в моей демонстрации, я узнал, если вы поместите код в
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
потерпит неудачу, потому что окно ключа было установлено еще.
Но я поместил ваш код в некоторые контроллеры представления
override func viewDidLoad() {
Это просто работает.
Лучшее решение для меня - это расширение с функцией. Создайте файл swift с этим расширением
Во-первых, это расширение 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
}
extension UIViewController {
func topMostViewController() -> UIViewController {
if self.presentedViewController == nil {
return self
}
if let navigation = self.presentedViewController as? UINavigationController {
return navigation.visibleViewController.topMostViewController()
}
if let tab = self.presentedViewController as? UITabBarController {
if let selectedTab = tab.selectedViewController {
return selectedTab.topMostViewController()
}
return tab.topMostViewController()
}
return self.presentedViewController!.topMostViewController()
}
}
extension UIApplication {
func topMostViewController() -> UIViewController? {
return self.keyWindow?.rootViewController?.topMostViewController()
}
}
Самый простой способ — взять последний из стека контроллеров представления:
if let viewController: UIViewController = navigationController.viewControllers.last {
// This `viewController` is on the top.
}