ios 在 AppDelegate.m 中获取当前显示在屏幕上的 UIViewController
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11637709/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get the current displaying UIViewController on the screen in AppDelegate.m
提问by lu yuan
The current UIViewController
on the screen need to response to push-notifications from APNs, by setting some badge views. But how could I get the UIViewController
in methodapplication:didReceiveRemoteNotification
: of AppDelegate.m
?
UIViewController
屏幕上的当前需要通过设置一些徽章视图来响应来自 APNs 的推送通知。但是我怎么能得到UIViewController
in 方法application:didReceiveRemoteNotification
: of AppDelegate.m
?
I tried use self.window.rootViewController
to get the current displaying UIViewController
, it may be a UINavigationViewController
or some other kind of view controller. And I find out that the visibleViewController
property of UINavigationViewController
can be used to get the UIViewController
on the screen. But what could I do if it is not a UINavigationViewController
?
我尝试使用self.window.rootViewController
获取当前显示UIViewController
,它可能是一个UINavigationViewController
或其他类型的视图控制器。我发现 的visibleViewController
属性UINavigationViewController
可用于UIViewController
在屏幕上显示 。但是,如果它不是 ,我该怎么办UINavigationViewController
?
Any help is appreciated! The related code is as following.
任何帮助表示赞赏!相关代码如下。
AppDelegate.m
AppDelegate.m
...
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
//I would like to find out which view controller is on the screen here.
UIViewController *vc = [(UINavigationViewController *)self.window.rootViewController visibleViewController];
[vc performSelector:@selector(handleThePushNotification:) withObject:userInfo];
}
...
ViewControllerA.m
视图控制器A.m
- (void)handleThePushNotification:(NSDictionary *)userInfo{
//set some badge view here
}
采纳答案by sergio
You can use the rootViewController
also when your controller is not a UINavigationController
:
rootViewController
当您的控制器不是 a 时,您也可以使用UINavigationController
:
UIViewController *vc = self.window.rootViewController;
Once you know the root view controller, then it depends on how you have built your UI, but you can possibly find out a way to navigate through the controllers hierarchy.
一旦你知道了根视图控制器,那么这取决于你如何构建你的 UI,但你可能会找到一种方法来浏览控制器层次结构。
If you give some more details about the way you defined your app, then I might give some more hint.
如果您提供有关定义应用程序的方式的更多详细信息,那么我可能会提供更多提示。
EDIT:
编辑:
If you want the topmost view(not view controller), you could check
如果你想要最顶层的视图(不是视图控制器),你可以检查
[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
although this view might be invisible or even covered by some of its subviews...
虽然这个视图可能是不可见的,甚至被它的一些子视图覆盖......
again, it depends on your UI, but this might help...
同样,这取决于您的用户界面,但这可能会有所帮助...
回答by zirinisp
I always love solutions that involve categories as they are bolt on and can be easily reused.
我总是喜欢涉及类别的解决方案,因为它们是固定的并且可以轻松重复使用。
So I created a category on UIWindow. You can now call visibleViewController on UIWindow and this will get you the visible view controller by searching down the controller hierarchy. This works if you are using navigation and/or tab bar controller. If you have another type of controller to suggest please let me know and I can add it.
所以我在 UIWindow 上创建了一个类别。您现在可以在 UIWindow 上调用 visibleViewController ,这将通过向下搜索控制器层次结构来获得可见视图控制器。如果您使用导航和/或标签栏控制器,这将起作用。如果您有其他类型的控制器建议,请告诉我,我可以添加它。
UIWindow+PazLabs.h (header file)
UIWindow+PazLabs.h(头文件)
#import <UIKit/UIKit.h>
@interface UIWindow (PazLabs)
- (UIViewController *) visibleViewController;
@end
UIWindow+PazLabs.m (implementation file)
UIWindow+PazLabs.m(实现文件)
#import "UIWindow+PazLabs.h"
@implementation UIWindow (PazLabs)
- (UIViewController *)visibleViewController {
UIViewController *rootViewController = self.rootViewController;
return [UIWindow getVisibleViewControllerFrom:rootViewController];
}
+ (UIViewController *) getVisibleViewControllerFrom:(UIViewController *) vc {
if ([vc isKindOfClass:[UINavigationController class]]) {
return [UIWindow getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]];
} else if ([vc isKindOfClass:[UITabBarController class]]) {
return [UIWindow getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]];
} else {
if (vc.presentedViewController) {
return [UIWindow getVisibleViewControllerFrom:vc.presentedViewController];
} else {
return vc;
}
}
}
@end
Swift Version
迅捷版
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
}
}
}
}
回答by Bart?omiej Semańczyk
Simple extension for UIApplication in Swift (cares even about moreNavigationController within UITabBarController
on iPhone):
Swift 中 UIApplication 的简单扩展(甚至关心UITabBarController
iPhone 上的moreNavigationController ):
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController
if let top = moreNavigationController.topViewController where top.view.window != nil {
return topViewController(top)
} else if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}
Simple usage:
简单用法:
if let rootViewController = UIApplication.topViewController() {
//do sth with root view controller
}
Works perfect:-)
工作完美:-)
UPDATE for clean code:
更新干净的代码:
extension UIViewController {
var top: UIViewController? {
if let controller = self as? UINavigationController {
return controller.topViewController?.top
}
if let controller = self as? UISplitViewController {
return controller.viewControllers.last?.top
}
if let controller = self as? UITabBarController {
return controller.selectedViewController?.top
}
if let controller = presentedViewController {
return controller.top
}
return self
}
}
回答by Aneil Mallavarapu
You could also post a notification via NSNotificationCenter. This let's you deal with a number of situations where traversing the view controller hierarchy might be tricky - for example when modals are being presented, etc.
您还可以通过 NSNotificationCenter 发布通知。这让您可以处理遍历视图控制器层次结构可能很棘手的许多情况 - 例如,当呈现模态时等。
E.g.,
例如,
// MyAppDelegate.h
NSString * const UIApplicationDidReceiveRemoteNotification;
// MyAppDelegate.m
NSString * const UIApplicationDidReceiveRemoteNotification = @"UIApplicationDidReceiveRemoteNotification";
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidReceiveRemoteNotification
object:self
userInfo:userInfo];
}
In each of your View Controllers:
在每个视图控制器中:
-(void)viewDidLoad {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(didReceiveRemoteNotification:)
name:UIApplicationDidReceiveRemoteNotification
object:nil];
}
-(void)viewDidUnload {
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:UIApplicationDidReceiveRemoteNotification
object:nil];
}
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
// see http://stackoverflow.com/a/2777460/305149
if (self.isViewLoaded && self.view.window) {
// handle the notification
}
}
You could also use this approach to instrument controls which need to update when a notification is received and are used by several view controllers. In that case, handle the add/remove observer calls in the init and dealloc methods, respectively.
您还可以使用这种方法来检测需要在收到通知时更新并被多个视图控制器使用的控件。在这种情况下,分别在 init 和 dealloc 方法中处理添加/删除观察者调用。
回答by Jeehut
Code
代码
Here's an approach using the great switch-case syntaxin Swift 3/4/5:
这是在Swift 3/4/5 中使用很棒的switch-case 语法的方法:
extension UIWindow {
/// Returns the currently visible view controller if any reachable within the window.
public var visibleViewController: UIViewController? {
return UIWindow.visibleViewController(from: rootViewController)
}
/// Recursively follows navigation controllers, tab bar controllers and modal presented view controllers starting
/// from the given view controller to find the currently visible view controller.
///
/// - Parameters:
/// - viewController: The view controller to start the recursive search from.
/// - Returns: The view controller that is most probably visible on screen right now.
public static func visibleViewController(from viewController: UIViewController?) -> UIViewController? {
switch viewController {
case let navigationController as UINavigationController:
return UIWindow.visibleViewController(from: navigationController.visibleViewController ?? navigationController.topViewController)
case let tabBarController as UITabBarController:
return UIWindow.visibleViewController(from: tabBarController.selectedViewController)
case let presentingViewController where viewController?.presentedViewController != nil:
return UIWindow.visibleViewController(from: presentingViewController?.presentedViewController)
default:
return viewController
}
}
}
The basic idea is the same as in zirinisp's answer, it's just using a more Swift 3+ like syntax.
基本思想与 zirinisp 的答案相同,它只是使用更像 Swift 3+ 的语法。
Usage
用法
You probably want to create a file named UIWindowExtension.swift
. Make sure it includes the import UIKit
statement, now copy the above extension code.
您可能想要创建一个名为UIWindowExtension.swift
. 确保它包含import UIKit
语句,现在复制上面的扩展代码。
On the call side it can be either used without any specific view controller:
在调用端,它可以在没有任何特定视图控制器的情况下使用:
if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
// do whatever you want with your `visibleViewCtrl`
}
Or if you know your visible view controller is reachable from a specific view controller:
或者,如果您知道可以从特定视图控制器访问您的可见视图控制器:
if let visibleViewCtrl = UIWindow.visibleViewController(from: specificViewCtrl) {
// do whatever you want with your `visibleViewCtrl`
}
I hope it helps!
我希望它有帮助!
回答by nvrtd frst
I have found that iOS 8 has screwed everything up. In iOS 7 there is a new UITransitionView
on the view hierarchy whenever you have a modally presented UINavigationController
. Anyway, here's my code that finds gets the topmost VC. Calling getTopMostViewController
should return a VC that you should be able to send a message like presentViewController:animated:completion
. It's purpose is to get you a VC that you can use to present a modal VC, so it will most likely stop and return at container classes like UINavigationController
and NOT the VC contained within them. Should not be hard to adapt the code to do that too. I've tested this code in various situations in iOS 6, 7 and 8. Please let me know if you find bugs.
我发现 iOS 8 把一切都搞砸了。在 iOS 7 中,UITransitionView
每当您以模态呈现UINavigationController
. 无论如何,这是我找到最顶层 VC 的代码。调用getTopMostViewController
应该返回一个 VC,您应该能够发送类似presentViewController:animated:completion
. 它的目的是为您提供一个 VC,您可以使用它来呈现一个模态 VC,因此它很可能会在容器类中停止并返回,UINavigationController
而不是其中包含的 VC。也应该不难调整代码来做到这一点。我已经在 iOS 6、7 和 8 的各种情况下测试了这段代码。如果你发现错误,请告诉我。
+ (UIViewController*) getTopMostViewController
{
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
if (window.windowLevel != UIWindowLevelNormal) {
NSArray *windows = [[UIApplication sharedApplication] windows];
for(window in windows) {
if (window.windowLevel == UIWindowLevelNormal) {
break;
}
}
}
for (UIView *subView in [window subviews])
{
UIResponder *responder = [subView nextResponder];
//added this block of code for iOS 8 which puts a UITransitionView in between the UIWindow and the UILayoutContainerView
if ([responder isEqual:window])
{
//this is a UITransitionView
if ([[subView subviews] count])
{
UIView *subSubView = [subView subviews][0]; //this should be the UILayoutContainerView
responder = [subSubView nextResponder];
}
}
if([responder isKindOfClass:[UIViewController class]]) {
return [self topViewController: (UIViewController *) responder];
}
}
return nil;
}
+ (UIViewController *) topViewController: (UIViewController *) controller
{
BOOL isPresenting = NO;
do {
// this path is called only on iOS 6+, so -presentedViewController is fine here.
UIViewController *presented = [controller presentedViewController];
isPresenting = presented != nil;
if(presented != nil) {
controller = presented;
}
} while (isPresenting);
return controller;
}
回答by jungledev
Way less code than all other solutions:
比所有其他解决方案更少的代码:
Objective-C version:
Objective-C 版本:
- (UIViewController *)getTopViewController {
UIViewController *topViewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
while (topViewController.presentedViewController) topViewController = topViewController.presentedViewController;
return topViewController;
}
Swift 2.0 version: (credit goes to Steve.B)
Swift 2.0 版本:(归功于 Steve.B)
func getTopViewController() -> UIViewController {
var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
while (topViewController.presentedViewController != nil) {
topViewController = topViewController.presentedViewController!
}
return topViewController
}
Works anywhere in your app, even with modals.
可在您的应用程序中的任何位置使用,甚至可以使用模态。
回答by Bobj-C
zirinisp's Answer in Swift:
zirinisp 在 Swift 中的回答:
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;
}
}
}
}
Usage:
用法:
if let topController = window.visibleViewController() {
println(topController)
}
回答by Neel Kamal
Specify title to each ViewController and then get the title of current ViewController by the code given below.
为每个 ViewController 指定标题,然后通过下面给出的代码获取当前 ViewController 的标题。
-(void)viewDidUnload {
NSString *currentController = self.navigationController.visibleViewController.title;
Then check it by your title like this
然后像这样通过你的标题检查它
if([currentController isEqualToString:@"myViewControllerTitle"]){
//write your code according to View controller.
}
}
回答by Nicolas Manzini
Mine is better! :)
我的更好!:)
extension UIApplication {
var visibleViewController : UIViewController? {
return keyWindow?.rootViewController?.topViewController
}
}
extension UIViewController {
fileprivate var topViewController: UIViewController {
switch self {
case is UINavigationController:
return (self as! UINavigationController).visibleViewController?.topViewController ?? self
case is UITabBarController:
return (self as! UITabBarController).selectedViewController?.topViewController ?? self
default:
return presentedViewController?.topViewController ?? self
}
}
}