【发布时间】:2020-12-08 23:45:30
【问题描述】:
我有一些viewControllers,但我不使用NavigationController。
如何在应用委托方法中获得可见的视图控制器(例如applicationWillResignActive)?
我从NSNotification 知道该怎么做,但我认为这是错误的方式。
【问题讨论】:
标签: ios
我有一些viewControllers,但我不使用NavigationController。
如何在应用委托方法中获得可见的视图控制器(例如applicationWillResignActive)?
我从NSNotification 知道该怎么做,但我认为这是错误的方式。
【问题讨论】:
标签: ios
这应该为你做:
- (void)applicationWillResignActive:(UIApplication *)application
{
UIViewController *vc = [self visibleViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)visibleViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil)
{
return rootViewController;
}
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self visibleViewController:lastViewController];
}
if ([rootViewController.presentedViewController isKindOfClass:[UITabBarController class]])
{
UITabBarController *tabBarController = (UITabBarController *)rootViewController.presentedViewController;
UIViewController *selectedViewController = tabBarController.selectedViewController;
return [self visibleViewController:selectedViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self visibleViewController:presentedViewController];
}
【讨论】:
@aviatorken89 的回答对我来说效果很好。我不得不把它翻译成 Swift - 对于任何开始使用 Swift 的人:
为 Swift 3 更新:
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
if rootVC?.presentedViewController == nil {
return rootVC
}
if let presented = rootVC?.presentedViewController {
if presented.isKind(of: UINavigationController.self) {
let navigationController = presented as! UINavigationController
return navigationController.viewControllers.last!
}
if presented.isKind(of: UITabBarController.self) {
let tabBarController = presented as! UITabBarController
return tabBarController.selectedViewController!
}
return getVisibleViewController(presented)
}
return nil
}
旧答案:
func applicationWillResignActive(application: UIApplication) {
let currentViewController = getVisibleViewController(nil)
}
func getVisibleViewController(var rootViewController: UIViewController?) -> UIViewController? {
if rootViewController == nil {
rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController
}
if rootViewController?.presentedViewController == nil {
return rootViewController
}
if let presented = rootViewController?.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 getVisibleViewController(presented)
}
return nil
}
【讨论】:
return rootViewController! 更改为return rootViewController,因为它可能为nil。
currentViewController 的值?例如,如果我想说if currentViewController == someViewController { //do this } else { //do that} 有没有办法?打印变量会给出名称,但会附加一个在构建之间不维护的标识符。
let currentViewController = getVisibleViewController(nil) if currentViewController.isKindOfClass(YourViewControllerClass) { ... } else ...
我们将其实现为 UIApplication 扩展:
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
}
}
【讨论】:
这是 Swift 4 中的一个答案,与公认的答案非常相似,但有一些改进:
在奇怪的情况下不会崩溃,即当标签栏控制器没有选定的视图控制器时。
static var visibleViewController: UIViewController? {
var currentVc = UIApplication.shared.keyWindow?.rootViewController
while let presentedVc = currentVc?.presentedViewController {
if let navVc = (presentedVc as? UINavigationController)?.viewControllers.last {
currentVc = navVc
} else if let tabVc = (presentedVc as? UITabBarController)?.selectedViewController {
currentVc = tabVc
} else {
currentVc = presentedVc
}
}
return currentVc
}
【讨论】:
AppDelegate 中,你会用AppDelegate.visibleViewController 来调用它。
这里的顶级建议在许多情况下都可以正常工作以获得'最佳猜测'解决方案,但通过一些小的调整,我们可以获得不依赖于您应用程序的更完整的解决方案查看层次结构实现。
1) Cocoa Touch 的视图层次结构允许多个子级同时出现和可见,因此我们需要改为询问当前可见的视图控制器(复数)并相应地处理结果
2) UINavigationControllers 和 UITabBarControllers 在 iOS 应用程序中很常用,但它们并不是唯一的容器视图控制器。 UIKit 还提供了UIPageViewController、UISplitViewController,并允许您编写自己的自定义容器视图控制器。
3) 我们可能希望忽略弹出模式和特定类型的视图控制器,例如 UIAlertControllers 或自定义嵌入式子视图控制器。
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 []
}
}
【讨论】:
这是 Swift 中一种递归的、面向协议的方法。可以扩展到自定义类型,但任何类型的 UIViewController 子类都应该使用下面的代码。
public protocol ViewControllerContainer {
var topMostViewController: UIViewController? { get }
}
extension UIViewController: ViewControllerContainer {
public var topMostViewController: UIViewController? {
if let presentedView = presentedViewController {
return recurseViewController(presentedView)
}
return childViewControllers.last.map(recurseViewController)
}
}
extension UITabBarController {
public override var topMostViewController: UIViewController? {
return selectedViewController.map(recurseViewController)
}
}
extension UINavigationController {
public override var topMostViewController: UIViewController? {
return viewControllers.last.map(recurseViewController)
}
}
extension UIWindow: ViewControllerContainer {
public var topMostViewController: UIViewController? {
return rootViewController.map(recurseViewController)
}
}
func recurseViewController(viewController: UIViewController) -> UIViewController {
return viewController.topMostViewController.map(recurseViewController) ?? viewController
}
【讨论】:
如果你使用的是 IQKeyboardManager,它们里面有一个扩展
这样你就可以了
application.keyWindow?.currentViewController? // <- there you go
所以把它添加到你的 pod 文件中
pod 'IQKeyboardManager'
then pod update and you are away!
希望对你有帮助
【讨论】:
这是@ProgrammierTier 答案的改进版本。如果您有一个嵌套在标签栏中的导航栏,您将使用@ProgrammierTier 的答案返回 UINavigationController。此外,展开的力也较小。这应该解决@Harendra-Tiwari 面临的问题。
Swift 4.2:
func getVisibleViewController(_ rootViewController: UIViewController?) -> UIViewController? {
var rootVC = rootViewController
if rootVC == nil {
rootVC = UIApplication.shared.keyWindow?.rootViewController
}
var presented = rootVC?.presentedViewController
if rootVC?.presentedViewController == nil {
if let isTab = rootVC?.isKind(of: UITabBarController.self), let isNav = rootVC?.isKind(of: UINavigationController.self) {
if !isTab && !isNav {
return rootVC
}
presented = rootVC
} else {
return rootVC
}
}
if let presented = presented {
if presented.isKind(of: UINavigationController.self) {
if let navigationController = presented as? UINavigationController {
return navigationController.viewControllers.last!
}
}
if presented.isKind(of: UITabBarController.self) {
if let tabBarController = presented as? UITabBarController {
if let navigationController = tabBarController.selectedViewController! as? UINavigationController {
return navigationController.viewControllers.last!
} else {
return tabBarController.selectedViewController!
}
}
}
return getVisibleViewController(presented)
}
return nil
}
【讨论】:
如果您的应用的根视图控制器是 UINavigationController,那么您可以使用它:
UIViewController *currentControllerName = ((UINavigationController*)appDelegate.window.rootViewController).visibleViewController;
如果你使用的是 UITabBarController,那么你可以使用这个:
UIViewController *currentControllerName = ((UITabBarController*)appDelegate.window.rootViewController).selectedViewController;
【讨论】:
这是 @ProgrammierTier 答案的 Swift 2.3 实现,作为 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
}
}
从applicationWillResignActive获取它
func applicationWillResignActive(application: UIApplication) {
let visibleVC = application.keyWindow?.rootViewController?.visibleViewController
}
【讨论】:
这只是一个受@krcjr89 回答启发的快速修复。 接受的答案不会一直向下导航。例如,如果您在标签栏控制器中嵌入了导航控制器,您将不会访问可见视图控制器,而是导航控制器。
我像@Christian 一样将它作为 UIApplication 的扩展,因为这是最有意义的。
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
}
}
【讨论】:
在 ObjC 中使用 UIViewController 类别的先前答案的修改版本:
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];
}
【讨论】:
在我的情况下,我有 Tabbar 控制器,然后是每个 Tab 的导航控制器,希望它可以帮助某人
UIViewController *loginViewController = self.window.rootViewController;
UITabBarController *controller = loginViewController.tabBarController;
UIViewController *currentController = controller.selectedViewController.childViewControllers.lastObject;
【讨论】:
从troop231修改
+ (UIViewController *)visibleViewController:(UIViewController *)rootViewController
{
if ([rootViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController *)rootViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self visibleViewController:lastViewController];
}
if ([rootViewController isKindOfClass:[UITabBarController class]])
{
UITabBarController *tabBarController = (UITabBarController *)rootViewController;
UIViewController *selectedViewController = tabBarController.selectedViewController;
return [self visibleViewController:selectedViewController];
}
if (rootViewController.presentedViewController != nil)
{
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self visibleViewController:presentedViewController];
}
return rootViewController;
}
【讨论】: