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.
Related
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;
}
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!
In essence what is the correct way of pushing a new view controller from a sub view of a navigation controller.
The issue being subviews of the navigation view don't inherit the self.navigationController (its nil)
The reason is I need separate controllers for the navigation bar view & the main view but both need to push new controllers.
I am willing to change this model if someone can tell me the correct way of doing this.
Also:
AppDelegate *del = (AppDelegate *)[UIApplication sharedApplication].delegate
[del.navigationController pushViewController:vc animated:YES];
Does not work as the delegates controller is nil.
Create a following category on UIView.
#interface UIView (GetUIViewController)
- (UIViewController *)viewController;
#end
#implementation UIView (GetUIViewController)
- (UIViewController *)viewController;
{
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else {
return nil;
}
}
#end
Now get the SuperView from the subView.
mySuperView = [mySubView superview];
Then call the method from category created.
mySuperViewController = [mySuperView viewController];
Now, using this viewController, you can access the navigationController.
I have not tried the above code and approach, but I hope it should work.
You may try two ways
1.Use the superViewController's navigationController to push your viewController
2.Embed your current viewController in a NavigationController so that the navigationController won't be nil
I am trying to create an application in Xcode that will switch to a new view when the phone is rotated from one orientation to another.
Here is the "switchviewcontroller.h" file code:
#import <UIKit/UIKit.h>
#interface SwitchViewController : UIViewController {
}
-(IBAction)switchview:(id)sender;
#end
And here is the "switchviewcontroller.m" file code:
#import "SwitchViewController.h"
#import "secondview.h"
#implementation SwitchViewController
-(IBAction)switchview:(id)sender {}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return YES;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
if((fromInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(fromInterfaceOrientation == UIInterfaceOrientationLandscapeRight))
{
[[secondview alloc] initWithNibName:#"secondview" bundle:[NSBundle mainBundle]];
}
}
It runs in iPhone simulator with no errors, but, when I rotate it doesn't load the new view. For starters I think I need the app to open in landscape mode, which I don't know how to do, but it still wouldn't work anyway and I think it has something to to with the "initWithNibName" part of the code because I have .xib files and not .nib files. Can anyone help me with these 2 things? Thanks.
You are not pushing or presenting anything, you are only init'ing the view.
secondview *second = [[secondview alloc] initWithNibName:#"secondview" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:second animated:YES];
However, its not a good place to show a new view.
If you want to show the same view in a different orientation, try something this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(interfaceOrientation == UIInterfaceOrientationLandscapeRight))){
self.view = landscape;
}else if(((interfaceOrientation == UIInterfaceOrientationPortrait) ||
(interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown))){
self.view = portrait;
}
return YES;
}
Note that portrait and landscape are UIViews in your UIViewController that you define in your header and connect through Interface Builder.
Additionaly, these need to be in .h / .m:
.h
IBOutlet UIView *portrait;
IBOutlet UIView *landscape;
#property(nonatomic,retain) UIView portrait;
#property(nonatomic,retain) UIView landscape;
.m
#synthesize portrait,landscape;
You've got to alloc and init a new view and replace you current viewcontroller's view with the new one.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) {
UIView *landscapeView = [[UIView alloc] init];
// Setup the landscape view here
self.view = landscapeView;
[landscapeView release];
}
else {
UIView *portraitView = [[UIView alloc] init];
// Setup the portrait view here
self.view = portraitView;
[portraitView release];
}
}
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
}