How to check if a view controller is presented modally or pushed on a navigation stack?
Solution 1
Take with a grain of salt, didn't test.
- (BOOL)isModal {
if([self presentingViewController])
return YES;
if([[[self navigationController] presentingViewController] presentedViewController] == [self navigationController])
return YES;
if([[[self tabBarController] presentingViewController] isKindOfClass:[UITabBarController class]])
return YES;
return NO;
}
Solution 2
In Swift:
Add a flag to test if it's a modal by the class type:
// MARK: - UIViewController implementation
extension UIViewController {
var isModal: Bool {
let presentingIsModal = presentingViewController != nil
let presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController
let presentingIsTabBar = tabBarController?.presentingViewController is UITabBarController
return presentingIsModal || presentingIsNavigation || presentingIsTabBar
}
}
Solution 3
You overlooked one method: isBeingPresented
.
isBeingPresented
is true when the view controller is being presented and false when being pushed.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if ([self isBeingPresented]) {
// being presented
} else if ([self isMovingToParentViewController]) {
// being pushed
} else {
// simply showing again because another VC was dismissed
}
}
Solution 4
Swift 5
Here is solution that addresses the issue mentioned with previous answers, when isModal()
returns true
if pushed UIViewController
is in a presented UINavigationController
stack.
extension UIViewController {
var isModal: Bool {
if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
return false
} else if presentingViewController != nil {
return true
} else if navigationController?.presentingViewController?.presentedViewController == navigationController {
return true
} else if tabBarController?.presentingViewController is UITabBarController {
return true
} else {
return false
}
}
}
It does work for me so far. If some optimizations, please share.
Solution 5
self.navigationController != nil would mean it's in a navigation stack.
In order to handle the case that the current view controller is pushed while the navigation controller is presented modally, I have added some lines of code to check if the current view controller is the root controller in the navigation stack .
extension UIViewController {
var isModal: Bool {
if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
return false
} else if presentingViewController != nil {
return true
} else if let navigationController = navigationController, navigationController.presentingViewController?.presentedViewController == navigationController {
return true
} else if let tabBarController = tabBarController, tabBarController.presentingViewController is UITabBarController {
return true
} else {
return false
}
}
}
meaning-matters
Should software be simple, usable, easy to understand, ...? I believe there's one unifying force that matters most: meaning. When focussing on what is most meaningful for users, properties like simplicity and usability will follow naturally. This principle can be applied to all levels: For example, from arranging code statements, to presenting data. It's my guide that has led to great results for already more than two decades. Cornelis
Updated on July 08, 2022Comments
-
meaning-matters almost 2 years
How can I, in my view controller code, differentiate between:
- presented modally
- pushed on navigation stack
Both
presentingViewController
andisMovingToParentViewController
areYES
in both cases, so are not very helpful.What complicates things is that my parent view controller is sometimes modal, on which the to be checked view controller is pushed.
It turns out my issue is that I embed my
HtmlViewController
in aUINavigationController
which is then presented. That's why my own attempts and the good answers below were not working.HtmlViewController* termsViewController = [[HtmlViewController alloc] initWithDictionary:dictionary]; UINavigationController* modalViewController; modalViewController = [[UINavigationController alloc] initWithRootViewController:termsViewController]; modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; [self presentViewController:modalViewController animated:YES completion:nil];
I guess I'd better tell my view controller when it's modal, instead of trying to determine.
-
ColdLogic almost 10 yearsCan still be in a modal navigation controller
-
JRG-Developer almost 10 years+1: Nice approach. :D This assumes, of course, that you care that
presentingViewController
'spresentedViewController
must matchself
(and not simply be an ancestor). -
meaning-matters almost 10 yearsI found this in another SO post. But, does not work if the pushed view controller's parent is a modal; which is the situation I'm having.
-
ColdLogic almost 10 yearsadded
if([self presentingViewController]) return YES;
-
ColdLogic almost 10 yearsIf
presentingViewController
is not nil, then either you or an ancestor is being presented modally. -
meaning-matters almost 10 yearsAs I wrote,
presentingViewController
is alwaysYES
in my case; does not help. -
ColdLogic almost 10 yearsWhat do you mean it doesn't work then? Is it returning yes when you expect it to return no?
-
meaning-matters almost 10 yearsI tried this too before posting, and it does not work,
isBeingPresented
isNO
. But I see the reason now, I'm embedding my presented view controller in aUINavigationController
, and that's the one I'm pushing. -
A . Radej almost 10 yearsSo 'modal' and 'pushed on navigation stack' are not mutually exclusive. Thinking this depends on the context, but checking if self.navigationController is not nil does answer whether it's a view controller of a navigation controller.
-
meaning-matters almost 10 yearsSee edits, I was embedding my view controller in a navigation controller, so all these methods were not working. Since you were the first with a in principle good answer you won all the points.
-
rmaddy almost 10 yearsYou can't push a navigation controller. Perhaps you meant that you are presenting the navigation controller.
-
rmaddy almost 10 years@Daniel The difference is between "pushed" and "presented". "Modal' has nothing to do with it. I believe "ColdLogic" meant "presented" when they said "modal".
-
Yevhen Dubinin almost 10 years
presentingViewController
returnsYES
for pushed VC, when there is aUITabBarController
being set as a root. So, does not suitable in my case. -
jowie about 9 yearsFor some reason,
self.isBeingPresented
is alwaysnil
for me. I've had to useself.presentingViewController
. I have no idea why. -
Admin almost 9 years@jowie: Please be aware of the method definition, it returns a
BOOL
, not anNSObject
=>- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);
-
jowie almost 9 years@SebastianKeller yes I know, but
po self.isBeingPresented
shouldn't returnnil
. If I test on other projects and different view controllers, the result of that statement isfalse
. My project is iOS 8.1 and above. -
rmaddy almost 9 years@jowie Use
p
, notpo
when printing a primitive value.po
is for printing objects. -
Morkrom almost 9 yearsThis property is deprecated.
-
funct7 almost 9 yearsDocumentation for
isBeingPresented
- This method returns YES only when called from inside the viewWillAppear: and viewDidAppear: methods. -
Lee over 8 yearsThis does not work if you present a view controller then it pushes another one.
-
meaning-matters almost 8 yearsYes, I meant that I present the
UINavigationController
. -
Terrence over 7 years@BridgeTheGap What documentation are you referring to? I don't see this in the UIViewController Overview nor on the property reference itself.
-
rmaddy over 7 years@Terrence It seems the latest documentation doesn't show that information but it used to be there. The
isBeingPresented
,isBeingDismissed
,isMovingFromParentViewController
andisMovingToParentViewController
are only valid inside the 4view[Will|Did][Disa|A]ppear
methods. -
Alexander Abakumov about 7 yearswhich you should always do anyway - please explain why?
-
E-Riddie over 6 yearsWell in general when you present modally, you put the viewController on a navigationController and you present it. If that is the case your statement would be wrong, however on the code this case is handled. Please improve your answer :)
-
Colin Swelin over 6 years"This does not work if you present a view controller then it pushes another one" That's not the intention of this, the pushed view controller isn't being presented.
-
nickdnk about 6 yearsAlexander, you shouldn't, really.
-
jk7 almost 6 years
isBeingPresented
works in other situations but seems to give inconsistent results. Ex. After presenting a popover from within the UITextFieldDelegate method, textField:shouldChangeCharactersInRange:replacementString:, isBeingPresented correctly returned YES while still within the scope of that method call, but the next time that method was called it return NO even though the popover was still being presented. -
malinois over 5 yearsShould be better in a var, like
var isModal: Bool {}
-
Eli Burke over 5 yearsSwift 4.2 / iOS 12. Still works well, but be aware that navigationController?.presentingViewController?.presentedViewController === navigationController will evaluate to true if both are nil (for example, if you call it on a view controller that has not yet been presented).
-
YannSteph over 5 years@malinois is changed
-
Cesare over 5 yearsUpvoted but didn't work on my end. I'm presenting a view controller modally but it returns false. Not sure why.
-
gklka about 5 yearsThis method does not work when you use same view controller class in multiple places, since it does check only the class of it. You can explicitly check the equality instead.
-
damjandd about 5 yearsWhat does the last
false
parameter in thereturn
statement do? -
Jean Raymond Daher almost 5 yearsgood job that deals with all the use cases. room for a bit of refactoring probably but still upvote !!
-
Hlung about 4 yearsWhy do you need to check
tabBarController?.presentingViewController is UITabBarController
? Does it matter if thatpresentingViewController
is also a UITabBarController? -
Hlung about 4 yearsAnd if navigationController is nil,
isModal
will returntrue
. Is this intended? -
famfamfam almost 4 yearsyou need change to let presentingIsNavigation = navigationController?.presentingViewController?.presentedViewController == navigationController && navigationController != nil
-
Tà Truhoada almost 3 yearsSwift 5: presentingIsNavigation = true if navigationController is nil
-
meaning-matters over 2 yearsThanks for this answer. I'll try to find time to check this. BTW, I think it's semantically better to put the second
return
statement in anelse { }
block because it's the opposite case of having a navigation controller.