How do I get reference to top visible view controller in my app. I saw some solutions which make use of navigationcontroller.[top|visible]viewcontroller. But I don't use navigation controllers in my app.
This seems like a pretty common use case, and I find it strange it is difficult to get access to top|visible view controller
This should also follow your modal views and navigation controllers (if any):
- (UIViewController *)deepestPresentedViewControllerOf:(UIViewController *)viewController
{
if (viewController.presentedViewController) {
return [self deepestPresentedViewControllerOf:viewController.presentedViewController];
} else {
return viewController;
}
}
- (UIViewController *)topViewController
{
UIViewController *rootViewController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
UIViewController *deepestPresentedViewController = [self deepestPresentedViewControllerOf:rootViewController];
if ([deepestPresentedViewController isKindOfClass:[UINavigationController class]]) {
return ((UINavigationController *)deepestPresentedViewController).topViewController;
} else {
return deepestPresentedViewController;
}
}
You should probably be using the delegate pattern here (giving the child view controller a reference to an object that it can call on). If you edit your post to explain why you think you need a reference to the top view controller, we can give you advice about how to use the delegate pattern in your situation.
But for now I'll just give you the rope you need to hang yourself:
UIViewController *topVC = [UIApplication sharedApplication].keyWindow.rootViewController;
-(UIViewController *)getCurrentViewController
{
UIViewController *result = nil;
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
if (window.windowLevel != UIWindowLevelNormal)
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for(UIWindow * tmpWin in windows)
{
if (tmpWin.windowLevel == UIWindowLevelNormal)
{
window = tmpWin;
break;
}
}
}
UIView *frontView = [[window subviews] objectAtIndex:0];
id nextResponder = [frontView nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
result = nextResponder;
else
result = window.rootViewController;
return result;
}
-(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;
}
-(UIWindow *) returnWindowWithWindowLevelNormal
{
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *topWindow in windows)
{
if (topWindow.windowLevel == UIWindowLevelNormal)
return topWindow;
}
return [UIApplication sharedApplication].keyWindow;
}
Related
I need to get the currently displayed controller anywhere, but all the information points to the following method
// 获取当前显示的Controller
+ (UIViewController *)currentViewController {
// Find best view controller
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
return [self findBestViewController:viewController];
}
+ (UIViewController*)findBestViewController:(UIViewController*)vc {
if (vc.presentedViewController) {
return [self findBestViewController:vc.presentedViewController];
} else if ([vc isKindOfClass:[UISplitViewController class]]) {
UISplitViewController* svc = (UISplitViewController*) vc;
if (svc.viewControllers.count > 0)
return [self findBestViewController:svc.viewControllers.lastObject];
else
return vc;
} else if ([vc isKindOfClass:[UINavigationController class]]) {
UINavigationController* svc = (UINavigationController*) vc;
if (svc.viewControllers.count > 0)
return [self findBestViewController:svc.topViewController];
else
return vc;
} else if ([vc isKindOfClass:[UITabBarController class]]) {
UITabBarController* svc = (UITabBarController*) vc;
if (svc.viewControllers.count > 0)
return [self findBestViewController:svc.selectedViewController];
else
return vc;
} else {
return vc;
}
}
The obtained controller is no problem to jump, such as push and present. But if I need to know the class of the currently displayed controller, if the current controller contains sub-controllers, I ca n’t know which one is currently displayed, causing the requirements corresponding to my page and popup to be unrealizable
TransitionViewForCurrentTransition is not set, presentation controller was dismissed during the presentation? (<_UIFullscreenPresentationController:
how to solve it, no exception is printing but above error show on calling
-(IBAction)presume:(id)sender
{
[self returnToRootViewController];
}
- (UIViewController*)topmostViewController
{
UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
while(vc.presentedViewController) {
vc = vc.presentedViewController;
}
return vc;
}
- (void)returnToRootViewController
{
UIViewController* vc = [self topmostViewController];
while (vc) {
if(vc.presentingViewController) {
if ([vc isKindOfClass:[CarDetailVC class]])
{
#try {
[vc dismissViewControllerAnimated:NO completion:^{}];
} #catch (NSException *exception) {
NSLog(#"exception=%#",exception);
} #finally {
}
}
}
vc = vc.presentingViewController;
}
}
Let's start with a more direct path to the target vc (the instance of CarDetailVC).
- (UIViewController*)vcWithClass:(Class)klass {
UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
while(![vc.presentedViewController isKindOfClass:klass]) {
vc = vc.presentedViewController;
}
return vc;
}
Now, get to it and dismiss the vc it presented.
- (void)returnToCarVC {
CarDetailVC *carVC =(CarDetailVC*) [self vcWithClass:[CarDetailVC self]];
UIViewController *presented = carVC.presentedViewController;
[presented dismissViewControllerAnimated:NO completion:^{}];
}
We know , if your viewController have been contain UINavigationController ,
you can find your current visible view controller by 'self.navigationController.visibleViewController' .
But I you present a view controller , how to find current visible controller ?
For Example :
code one :
------
AVClr *avclr = [[AVClr alloc]init] ;
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate ;
appDelegate.window.rootViewController = avclr ;
[avclr presentViewController:loginNavClr animated:YES completion:nil] ;
---> now , display avclr
code two:
------
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate ;
UIViewController *currentVisibleViewController = appDelegate.window.rootViewController ;
BVClr *bvclr = [[BVClr alloc]init] ;
[currentVisibleViewController presentViewController:bvclr animated:YES completion:nil] ;
---> now , display bvclr
code three:
------
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate ;
UIViewController *currentVisibleViewController = appDelegate.window.rootViewController ;
CVClr *cvclr = [[CVClr alloc]init] ;
[currentVisibleViewController presentViewController:cvclr animated:YES completion:nil] ;
---> Error , can not display cvclr , because avclr is a rootViewController and avclr present bvclr , so display bvclr .
Question:
But we know ,code three in another .m file , so we don't know who is the rootViewController . so If I present cvclr , the result is unexpect !
In the circumstances ,how to display cvclr
-(UIViewController *)getVisibleViewController : (UIViewController *)rootViewController
{
UIViewController *rootVC = rootViewController;
if (rootVC == nil)
{
rootVC = [[[UIApplication sharedApplication] keyWindow] rootViewController];
}
if ([rootVC presentedViewController] == nil)
{
return rootVC;
}
if ([rootVC presentedViewController] != nil)
{
if ([[rootVC presentedViewController] isKindOfClass:UINavigationController.self]) {
UINavigationController *navigationController = (UINavigationController *)[rootVC presentedViewController];
return [[navigationController viewControllers] lastObject];
}
return [self getVisibleViewController : [rootVC presentedViewController]];
}
return nil;
}
to find current top view controller i used this method
- (UIViewController *)currentTopViewController
{
UIViewController *topVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
while (topVC.presentedViewController)
{
topVC = topVC.presentedViewController;
}
if ([topVC isKindOfClass:[UINavigationController class]]) {
return [(UINavigationController *)topVC topViewController];
}
return topVC;
}
If you are presenting the next screen from that class then you don't need to fetch top view controller from UIWindow
Simply use this..
-----------------
AVClr *avclr = [[AVClr alloc]init];
[self presentViewController: avclr animated:YES completion:nil] ;
------------------------------
BVClr *bvclr = [[BVClr alloc]init] ;
[self.presentingViewControler presentViewController:bvclr animated:YES completion:nil] ;
------------------
CVClr *cvclr = [[CVClr alloc]init] ;
[self.presentingViewControler presentViewController:cvclr animated:YES completion:nil] ;
This code also check UITabbarViewContoller :
-(UIViewController *) getVisibleViewContoller {
UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
if (!rootViewController) {
return nil;
}
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabbarVC = (UITabBarController *) rootViewController;
UIViewController *selectedVC = tabbarVC.selectedViewController;
if (selectedVC) {
if (![selectedVC isKindOfClass:[UINavigationController class]]) {
return selectedVC;
}
rootViewController = selectedVC;
}
}
if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationVC = (UINavigationController *) rootViewController;
if (navigationVC.topViewController) {
return navigationVC.topViewController;
}
return navigationVC.viewControllers.lastObject;
}
return rootViewController;
}
I am automating IOS app test cases using Appium. I have a scenario in which I have to identify.. i am on which screen so that I can close the screen
In broader view , we have a inapp notification in our app which get open in random way.
So I can handle these condition if I would be able to identify the current screen.
I handle the same thing in Android using driver.currentActivity method
I need the some kind of same method for IOS apps
you get top most view controller by using below method
+ (UIViewController*) topMostController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
Or if you have navigation or UIabbar controller then use below method to get visible view controller
- (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];
}
The iOS Sharekit working with previous Xcode but will 4.2 it's not working anymore, When I hit the Cancel button it goes to this routine
Inside the SHK.m
- (void)hideCurrentViewControllerAnimated:(BOOL)animated
{
if (isDismissingView)
return;
if (currentView != nil)
{
// Dismiss the modal view
if ([currentView parentViewController] != nil)
{
self.isDismissingView = YES;
[[currentView parentViewController] dismissModalViewControllerAnimated:animated];
}
else
self.currentView = nil;
}
}
I stepped the code and it just hit if (isDissmissingView) and it just return.
So, I manually inserted the code
[[currentView parentViewController] dismissModalViewControllerAnimated:animated];
to the top of the routine but this doesn't do anything.
I also includes some other codes for reference
- (void)showViewController:(UIViewController *)vc
{
if (rootViewController == nil)
{
// Try to find the root view controller programmically
// Find the top window (that is not an alert view or other window)
UIWindow *topWindow = [[UIApplication sharedApplication] keyWindow];
if (topWindow.windowLevel != UIWindowLevelNormal)
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for(topWindow in windows)
{
if (topWindow.windowLevel == UIWindowLevelNormal)
break;
}
}
UIView *rootView = [[topWindow subviews] objectAtIndex:0];
id nextResponder = [rootView nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
self.rootViewController = nextResponder;
else
NSAssert(NO, #"ShareKit: Could not find a root view controller. You can assign one manually by calling [[SHK currentHelper] setRootViewController:YOURROOTVIEWCONTROLLER].");
}
// Find the top most view controller being displayed (so we can add the modal view to it and not one that is hidden)
UIViewController *topViewController = [self getTopViewController];
if (topViewController == nil)
NSAssert(NO, #"ShareKit: There is no view controller to display from");
// If a view is already being shown, hide it, and then try again
if (currentView != nil)
{
self.pendingView = vc;
[[currentView parentViewController] dismissModalViewControllerAnimated:YES];
return;
}
// Wrap the view in a nav controller if not already
if (![vc respondsToSelector:#selector(pushViewController:animated:)])
{
UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:vc] autorelease];
if ([nav respondsToSelector:#selector(modalPresentationStyle)])
nav.modalPresentationStyle = [SHK modalPresentationStyle];
if ([nav respondsToSelector:#selector(modalTransitionStyle)])
nav.modalTransitionStyle = [SHK modalTransitionStyle];
nav.navigationBar.barStyle = nav.toolbar.barStyle = [SHK barStyle];
[topViewController presentModalViewController:nav animated:YES];
self.currentView = nav;
}
// Show the nav controller
else
{
if ([vc respondsToSelector:#selector(modalPresentationStyle)])
vc.modalPresentationStyle = [SHK modalPresentationStyle];
if ([vc respondsToSelector:#selector(modalTransitionStyle)])
vc.modalTransitionStyle = [SHK modalTransitionStyle];
[topViewController presentModalViewController:vc animated:YES];
[(UINavigationController *)vc navigationBar].barStyle =
[(UINavigationController *)vc toolbar].barStyle = [SHK barStyle];
self.currentView = vc;
}
self.pendingView = nil;
}
(void)hideCurrentViewController
{
[self hideCurrentViewControllerAnimated:YES];
}
This is a known error, which showed up on ios5. This has been fixed a long time ago in ShareKit 2.0. If you decide to upgrade, make sure to follow new install wiki very carefully and literally, since many things have changed comparing to the original sharekit
.