I'm trying to disable screen rotation in just one ViewController. I'm using this to change screen orientation to portrait:
-(void)viewDidAppear:(BOOL)animated{
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
}
and I'm disabling rotation like this:
- (BOOL)shouldAutorotate{
return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
return navigationController.topViewController.supportedInterfaceOrientations;
}
but it's not working. It rotates screen to portrait but it does't lock it, if I turn device it changes screen orientation.
You can try this code:
-(BOOL)shouldAutorotate
{
return NO;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
The above code will only work with UIViewControllers not UINavigationController stacks. If you are using a UINavigationController you should do the following:
Solution 1:
Add to AppDelegate.h a variable: #property (nonatomic , assign) bool blockRotation;
Add to AppDelegate.m function:
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (self.blockRotation) {
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAll;
}
In controller want disable add this code:
#import "AppDelegate.h"
//Put to `viewDidload`
AppDelegate* shared=[UIApplication sharedApplication].delegate;
shared.blockRotation=YES;
Solution 2: you can follow this answer: Hanling orientation
If you want to temporarily disable automatic rotation, avoid manipulating the orientation masks to do this. Instead, override the shouldAutorotate method on the initial view controller. This method is called before performing any autorotation. If it returns NO, then the rotation is suppressed.
So you need to subclass 'UINavigationController', implement shouldAutorotate and use your navigation controller class in your storyboard.
- (BOOL)shouldAutorotate
{
id currentViewController = self.topViewController;
if ([currentViewController isKindOfClass:[DetailViewController class]])
return NO;
return YES;
}
Related
since iOS6 i have big problems with the rotation. I implemented all the new
rotation methods (shouldAutorotate, preferredInterfaceOrientationForPresentation, supportedInterfaceOrientation), but all views are still rotating. The funny thing is
that the views are keeping their sizes and the rest of the Window (in Landscape) is black.
Thats the way i implement it, is there anything wrong?
#pragma mark -
#pragma mark - InterfaceOrientation iOS 5
//Deprecated in iOS 6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark -
#pragma mark - InterfaceOrientation iOS 6
- (BOOL)shouldAutorotate{
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortraitUpsideDown;
}
Thanks for your help folks.
I solved the Problem by making a category for the navigationcontroller:
#implementation UINavigationController (iOS6fix)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
Thanks to everyone for the answers!
I had that too, it rotate even if I turnes it to NO.
Two options:
go to your Project settings and change the possible orientations
Remove all the methods for AUTOROTATE. Even if they are set to NO, they rotate for me.
I used the following category on UIViewController to have landscape only on iOS5 and 6. Maybe it helps someone.
#import <UIKit/UIKit.h>
#implementation UIViewController (iOS6fix)
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Make sure your window to assgin the view controller object to rootViewController, Please don't add controller view as a sub view on window.
self.window.rootViewController = viewController;
}
I only want to support different Orientations on one View in my UINavigationController Stack. How can I do this?
It also has to work in iOS5.
I've had a lot of trouble with how iOS6 handles Orientation, hopefully this is what you're looking for.
Create a category of UINavigationController and call it "UINavigationController+autoRotate".
Put this in your UINavigationController+autoRotate.h:
#import <UIKit/UIKit.h>
#interface UINavigationController (autoRotate)
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
-(BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
#end
Put this in UINavigationController+autoRotate.m:
#import "UINavigationController+autoRotate.h"
#implementation UINavigationController (autoRotate)
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return [self.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
- (BOOL)shouldAutorotate
{
return [self.visibleViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
if (![[self.viewControllers lastObject] isKindOfClass:NSClassFromString(#"ViewController")])
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
else
{
return [self.topViewController supportedInterfaceOrientations];
}
}
#end
For Views that you DO NOT want to rotate, add:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
And for Views you DO want to rotate:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIDeviceOrientationPortraitUpsideDown);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (BOOL)shouldAutorotate
{
return YES;
}
In your App's delegate, add:
- (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
I recommend you to NOT create a category on UINavigationController to override those methods. Categories are not aimed to do that, and there is no warranty that your code is going to be loaded instead of Apple's one (even if actually that works). I advise you to create a subclass of UINavigationController, and override those methods in it.
The solution does not work on iOS 6 (ok on iOS 5) when:
vc A is supporting only portrait orientation
vc B is supporting all orientations
we push vc B from vc A, rotate vc B (e.g. in landscape) and pop back to vc A. vc A orientation stays in landscape mode...
I am using iOS 6.0 beta and my rotations do not work anymore.
Where can I set a UINavigationControllers supportedOrientations?
According to this http://news.yahoo.com/apple-ios-6-beta-3-changes-182849903.html
a UINavigation Controller does not consult their children to determine whether they should autorotate.
I am not using shouldAutorotateToInterfaceOrientation: anymore as it is deprecated.
Instead I am using supportedInterfaceOrientations: and shouldAutoRotate: and they are working fine until I place a ViewController into a NavigationController (as a Child).
From then on the orientations specified in the ViewController do not work anymore.
It seems it is using the orientations set by the navigation controller (UIInterfaceOrientationMaskAllButUpsideDown)
How can I set the InterfaceOrientations for the NavigationController so that my ViewControllers are locked to Portrait-Orientation?
Do I have to subclass UINavigationController and set the InterfaceOrientations there? Isn't it bad practise to subclass UINavigationController still in iOS 6.0?
Thanks for you help heaps!
Cheers!
If you want it to consult it's children again you can add a category to UINavigationController
#implementation UINavigationController (Rotation_IOS6)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
Subclass UINavigationController
//OnlyPortraitNavigationController.h
#interface OnlyPortraitNavigationController : UINavigationController
//OnlyPortraitNavigationController.m
#implementation OnlyPortraitNavigationController
- (BOOL)shouldAutorotate {
return NO;
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait; //for locked only Portrait
}
present new subclass navigationController with your portrait ViewController
SomeViewController *onlyPortraitVC = [[SomeViewController alloc]init];
OnlyPortraitNavigationController *portraitNav = [[OnlyPortraitNavigationController alloc]initWithRootViewController:onlyPortraitViewController];
[self presentViewController:portraitNav animated:YES completion:NULL];
this is work on my app hope it can help you.
Ack! I had my tabbar rotation issues resolved finally in iOS 5, but iOS 6 and xcode seem to have broken things... here is what I have:
Target App Summary includes: Supported Interface Orientations - Portraint, Landscape Left, Landscape Right
Every Single View in the App has the following methods:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return ((interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown) &&
(interfaceOrientation != UIInterfaceOrientationLandscapeLeft) &&
(interfaceOrientation != UIInterfaceOrientationLandscapeRight));
} else {
return YES;
}
}
- (BOOL)shouldAutorotate
{
NSLog(#"am I called1?");
return NO;
}
-(NSUInteger)supportedInterfaceOrientations{
NSLog(#"am I called?");
return UIInterfaceOrientationMaskPortrait;
}
In the views that are not part of the tab bar, rotation is blocked. In ALL the views of the tabbar (there are 5) the app never calls ShouldAutorotate and so rotates. It does seem supportedInterfaceOrientations gets called once when a view loads, but not when it appears if I switch between views, because I get the NSLog, but it seems to ignore the MaskPortrait setting.
I have to leave the landscape enabled in the target because I have a single video player view that needs to rotate (and it does so, fine)
Is this a tabbar bug in iOS 6? Do I need to disable the rotation of the views differently? The shouldautorotatetointerfaceorientation worked great in ios 5
I've been at it for a while
Thanks,
Zack
Zack, I ran into this same issue. It's because you have your viewController embedded inside of a TabBar Controller or UINavigationController and the calls to these methods are happening inside those instead of your normal View (Changed in iOS6).
I ran into this issue because I was presenting a viewController embedded inside a UINavigationController on all my modal views that had Navigation to different views (Signup Process, Login, etc).
My simple fix was to create a CATEGORY for UINavigationController that includes these two methods. I have shouldAutorotate returning NO anyway because I don't want my modal views rotating. Your fix may be this simple, give it a try. Hope it helps.
I created a category and named it autoRotate and selected theUINavigationController option. The M+H file are below.
#import "UINavigationController+autoRotate.h"
#implementation UINavigationController (autoRotate)
-(BOOL)shouldAutorotate {
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#end
... and the category .h:
#import <UIKit/UIKit.h>
#interface UINavigationController (autoRotate)
-(BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
#end
If you have a tab bar like I did, the only thing you need to do is to add the following to your delegate .m file,
#import "AppDelegate.h"
//UITabBarController category to set the view rotations for ios 6
#implementation UITabBarController (Background)
-(BOOL)shouldAutorotate
{
//I don't want to support auto rotate, but you can return any value you want here
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
//I want to only support portrait mode
return UIInterfaceOrientationMaskPortrait;
}
#end
/////here starts the implementation of the app delegate which is gonna be whatever you currently have on your .m delegate
#implementation AppDelegate
// delegate methods and other stuff
#end
I also had the issue that I needed some views to rotate and others not with several Navigation Controllers. I did this by telling the NavigationController to look in the view controller. Here is what I did.
I create a UINavigationController class called RootNavigationController and designated that class as the Custom Class for the Navigation Controllers in storyboard. In the RootNavigationController.m I added the following methods;
- (BOOL)shouldAutorotate {
return [self.visibleViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations {
return [self.visibleViewController supportedInterfaceOrientations];
}
In each view controller .m file I also added the following methods.
- (BOOL)shouldAutorotate {
//return yes or no
}
- (NSUInteger)supportedInterfaceOrientations{
//return supported orientation masks
}
Doing this allows me to set orientation for each view in its view controller.
Worked for me anyway…
My situation is:
UITabBarController has 2 items: 2 Navigation controller
UINavigationController1 withRootView: ViewController1
UINavigationController2 withRootView: ViewController2.
Now i want ViewController1 set shouldAutorotate:NO/maskPortrait and
ViewController2 set shouldAutorotate/MaskAll.
So my implement: Create UITabBarController category and UINavigationCntroller category like
UITabBarController+autoRotate.h
#interface UITabBarController (autoRotate)
-(BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
#end
UITabBarController+autoRotate.m
#import "UITabBarController+autoRotate.h"
#implementation UITabBarController (autoRotate)
- (BOOL)shouldAutorotate {
return [self.selectedViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations {
return [self.selectedViewController supportedInterfaceOrientations];
}
#end
UINavigationController+autoRotate.h
#interface UINavigationController (autoRotate)
-(BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
#end
UINavigationController+autoRotate.m
#implementation UINavigationController (autoRotate)
- (BOOL)shouldAutorotate {
return [self.visibleViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations {
return [self.visibleViewController supportedInterfaceOrientations];
}
#end
UIViewController1.m
- (BOOL)shouldAutorotate {
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;;
}
UIViewController2.m
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
It worked like a charm!
In my case, I had a navigation controller embedded within a UITabBarController, and what worked was creating a category like Kunani defined, but extendind UITabBarController instead of UINavigationController. It worked like a charm :)
#import "UINavigationController+autoRotate.h"
#implementation UINavigationController (autoRotate)
-(BOOL)shouldAutorotate {
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#end
And .h file:
#import <UIKit/UIKit.h>
#interface UINavigationController (autoRotate)
-(BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
#end
This will do it in Swift
extension UITabBarController {
public override func shouldAutorotate() -> Bool {
if let selected = self.selectedViewController {
return selected.shouldAutorotate()
} else {
return false
}
}
public override func supportedInterfaceOrientations() -> Int {
if let selected = self.selectedViewController {
return selected.supportedInterfaceOrientations()
} else {
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
}
}
extension UINavigationController {
public override func shouldAutorotate() -> Bool {
return self.visibleViewController.shouldAutorotate()
}
public override func supportedInterfaceOrientations() -> Int {
return self.visibleViewController.supportedInterfaceOrientations()
}
}
https://stackoverflow.com/a/30632505/2298002
there are a lot of ambiguous or unforeseen results when handling this situation, especially when it comes to maintaining the orientation for only a specific view controller and not effecting the rest of the app. this algorithm seems to hand all for me
https://stackoverflow.com/a/30632505/2298002
You should add this thing in the UIViewController1.m to make sure that the previous orientation status is reconstructed:
(void)viewDidAppear:(BOOL)animated
{
[[UIDevice currentDevice] performSelector:NSSelectorFromString(#"setOrientation:")
withObject:(id)UIInterfaceOrientationPortrait];
}
Perfect!
We have common views that we use in our application in many locations inside of UINavigationControllers. Occasionally the UINavigationControllers are inside of popover views. Now the views we put into the nav controllers modify their navigation controller's toolbar buttons and, in some cases, use custom buttons that we've created. We need to be able to figure out from the UIViewcontroller itself if the view is inside of a popoverview so we can display the correctly colored buttons.
We can easily get the Navigation controller reference from the UIViewController, using UIViewController.navigationController, but there doesn't seem to be anything for finding a UIPopoverController.
Does anyone have any good ideas for how to do this?
Thanks!
As Artem said we have UIPopoverPresentationController since iOS8. To determine if view is in popover you can use its .arrowDirection property for example.
Check it in viewWillApear() of presented view controller:
// get it from parent NavigationController
UIPopoverPresentationController* popoverPresentationVC = self.parentViewController.popoverPresentationController;
if (UIPopoverArrowDirectionUnknown > popoverPresentationVC.arrowDirection) {
// presented as popover
} else {
// presented as modal view controller (on iPhone)
}
Here's another solution; define a protocol (e.g. PopoverSensitiveController) that has only one method:
#import "Foundation/Foundation.h"
#protocol PopoverSensitiveController
-(void) setIsInPopover:(BOOL) inPopover;
#end
A view controller that wants to know if it is in a popover then defines a property isInPopover; for example:
#import
#import "PopoverSensitiveController.h"
#pragma mark -
#pragma mark Interface
#interface MyViewController : UIViewController {
}
#pragma mark -
#pragma mark Properties
#property (nonatomic) BOOL isInPopover;
#pragma mark -
#pragma mark Instance Methods
...other stuff...
#end
Finally, in the splitView delegate (the assumption is that your app uses a split view controller):
#import "MySplitViewControllerDelegate.h"
#import "SubstitutableDetailViewController.h"
#import "PopoverSensitiveController.h"
#pragma mark -
#pragma mark Implementation
#implementation MySplitViewControllerDelegate
#pragma mark -
#pragma mark UISplitViewControllerDelegate protocol methods
-(void) splitViewController:(UISplitViewController *) splitViewController willHideViewController:(UIViewController *) aViewController withBarButtonItem:(UIBarButtonItem *) barButtonItem forPopoverController:(UIPopoverController *) pc {
// Keep references to the popover controller and the popover button, and tell the detail view controller to show the button
popoverController = [pc retain];
popoverButtonItem = [barButtonItem retain];
if ([[splitViewController.viewControllers objectAtIndex:1] respondsToSelector:#selector(showRootPopoverButtonItem:)]) {
UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
[detailViewController showRootPopoverButtonItem:barButtonItem];
}
if ([[splitViewController.viewControllers objectAtIndex:1] respondsToSelector:#selector(showRootPopoverButtonItem:)]) {
UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
[detailViewController showRootPopoverButtonItem:barButtonItem];
}
// If the view controller wants to know, tell it that it is a popover
if ([aViewController respondsToSelector:#selector(setIsInPopover:)]) {
[(id) aViewController setIsInPopover:YES];
}
// Make sure the proper view controller is in the popover controller and the size is as requested
popoverController.contentViewController = aViewController;
popoverController.popoverContentSize = aViewController.contentSizeForViewInPopover;
}
-(void) splitViewController:(UISplitViewController *) splitViewController willShowViewController:(UIViewController *) aViewController invalidatingBarButtonItem:(UIBarButtonItem *) barButtonItem {
// Tell the detail view controller to hide the button.
if ([[splitViewController.viewControllers objectAtIndex:1] respondsToSelector:#selector(invalidateRootPopoverButtonItem:)]) {
UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
[detailViewController invalidateRootPopoverButtonItem:barButtonItem];
}
// If the view controller wants to know, tell it that it is not in a popover anymore
if ([aViewController respondsToSelector:#selector(setIsInPopover:)]) {
[(id) aViewController setIsInPopover:NO];
}
// Now clear out everything
[popoverController release];
popoverController = nil;
[popoverButtonItem release];
popoverButtonItem = nil;
}
-(void) setPopoverButtonForSplitViewController:(UISplitViewController *) splitViewController {
// Deal with the popover button
UIViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
[detailViewController showRootPopoverButtonItem:popoverButtonItem];
// If the view controller wants to know, tell it that it is a popover (initialize the controller properly)
if ([[splitViewController.viewControllers objectAtIndex:0] respondsToSelector:#selector(setIsInPopover:)]) {
[(id) [splitViewController.viewControllers objectAtIndex:0] setIsInPopover:YES];
}
}
Then where ever in the view controller you want to know if you are in a popover, simply use the isInPopover property.
In iOS8 you can use popoverPresentationController property of UIViewController to check if it is contained in a popover presentation controller. From documentation, it returns: "The nearest ancestor in the view controller hierarchy that is a popover presentation controller. (read-only)"
I was recently looking for a way to determine wether or not a view was being displayed in a popover. This is what I came up with:
UIView *v=theViewInQuestion;
for (;v.superview != nil; v=v.superview) {
if (!strcmp(object_getClassName(v), "UIPopoverView")) {
NSLog(#"\n\n\nIM IN A POPOVER!\n\n\n\n");
}
Basically you climb the view's superview tree looking to see if any of its superviews is a UIPopoverView. The one caveat here is that the class UIPopoverView is an undocumented private class. I'm relying on the fact that the class name won't change in the future. YMMV.
In your case:
theViewInQuestion = theViewControllerInQuestion.view;
I'd be interested to see if anyone else comes up with a better solution.
Modification of the accepted answer for iOS5.1 and newer:
for (UIView *v = self.view; v.superview != nil; v=v.superview) {
if ([v isKindOfClass:[NSClassFromString(#"_UIPopoverView") class]]) {
NSLog(#"\n\n\nIM IN A POPOVER!\n\n\n\n");
}
}
** NOTE **
See comments about the reliability of this code.
My approach for this: (available with iOS 8 or greater)
- (BOOL)isContainedInPopover
{
UIPopoverPresentationController* popoverPresentationVC = self.parentViewController.popoverPresentationController;
return (popoverPresentationVC != nil);
}
Parent view controller will be the navigation controller which if inside a popover, will have a non-nil popoverPresentationController property.
By working with SpareTime's code I came to this, which works as expected. Nice code, nice solution:
Using the standard UISplitViewController example.
/* MasterViewController.h */
#import "UIPopoverViewDelegate.h"
#interface masterViewController : UITableViewController <UIPopoverViewDelegate>
#property (nonatomic) BOOL isInPopover;
#end
/* MasterViewController.m */
#import "MasterViewController.h"
#implementation MasterViewController
#synthesize isInPopover = _isInPopover;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.isInPopover)
{
// Code for appearing in popover
}
else
{
// Code for not appearing in popover
}
}
#end
/* DetailViewController.h */
#import "UIPopoverViewDelegate.h"
#interface detailViewController : UIViewController <UISplitViewControllerDelegate>
#end
/* DetailViewController.m */
#import "DetailViewController.h"
#implementation detailViewController
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
/* This method is called when transitioning to PORTRAIT orientation. */
UIViewController *hiddenViewController = [(UINavigationController *)viewController childViewControllers].lastObject;
if ([hiddenViewController respondsToSelector:#selector(setIsInPopover:)])
[(id <UIPopoverViewDelegate>)hiddenViewController setIsInPopover:YES];
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
/* This method is called when transitioning to LANDSCAPE orientation. */
UIViewController *shownViewController = [(UINavigationController *)viewController childViewControllers].lastObject;
if ([shownViewController respondsToSelector:#selector(setIsInPopover:)])
[(id <UIPopoverViewDelegate>)shownViewController setIsInPopover:NO];
}
#end
/* UIPopoverViewDelegate.h */
#protocol UIPopoverViewDelegate
#required
-(void)setIsInPopover:(BOOL)inPopover;
#end
In case that someone else is still looking for a solution i came up with one good enough for me.
Just override this method
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
(controller.presentedViewController as? YourViewControler).isPopover = false
return controller.presentedViewController
}
Here is an example of YourViewController
class AdvisorHomeFilterViewController: UIViewController {
// MARK: - Properties
var isPopover = true
}
If it is popover it will not call 'viewControllerForAdaptivePresentationStyle' method and it will stay true, in case it is not popover it will set it to false.
I wanted to put up a button in the view if the view wasn't displayed in a popover. I know the width of the popover because I just set it. So i can test whether I'm on an iPad and if the width of the frame is the same as what I set.
- (void)viewWillAppear:(BOOL)animated {
[self setContentSizeForViewInPopover:CGSizeMake(400, 500)];
NSInteger frameWidth = self.view.frame.size.width;
//Let you go back to the game if on an iPod.
if ( ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) && !(frameWidth == 400) ) { ---code to display a button --}
All these 'Exact Classname Matching Approaches' are very prone to fail and break at even the slightest changes Apple will make. Also doing one-char-vars and cryptic for-loops is not exactly a solution fitting to my style.
I use followingpiece of code:
- (BOOL) isInPopOver {
UIView *currentView = self.view;
while( currentView ) {
NSString *classNameOfCurrentView = NSStringFromClass([currentView class]);
NSLog( #"CLASS-DETECTED: %#", classNameOfCurrentView );
NSString *searchString = #"UIPopoverView";
if( [classNameOfCurrentView rangeOfString:searchString options:NSCaseInsensitiveSearch].location != NSNotFound ) {
return YES;
}
currentView = currentView.superview;
}
return NO;
}
All the solutions above seems a little bit complicated. I'm using a variable called isInPopover which I set to true if the view controller is presented in a popover. In the view controller in popoverControllerDidDismissPopover or in viewWillDisappear I set the boolean value to false. It does work and is very simple.
Since self.popoverPresentationController is created lazily in most recent iOS versions, one should check for nil-ness of self.popoverPresentationController.presentingViewController, if not nil this would mean self is currently presented in a popover.
Swift 4 version (function can be added in extension UIViewController):
func isInPopover() -> Bool {
guard UIDevice.current.userInterfaceIdiom == .pad else { return false }
var checkingVC: UIViewController? = self
repeat {
if checkingVC?.modalPresentationStyle == .popover {
return true
}
checkingVC = checkingVC?.parent
} while checkingVC != nil
return false
}