I am using UIImagePickerController to select a single image from photo library. There is a strange issue on iPad when it is in landscape mode.
The image picker is presented using UIPopoverPresentationController on iPad as recommended. When it is first presented, the status bar is correct:
However, when going into the second level of the photo library, the status bar is changed to portrait mode:
What I have noticed so far are:
This issue only appears in iOS 11, not iOS 10.
When it happens, rotate the iPad to portrait and back to landscape will fix the status bar orientation.
It only happened the first time presenting the picker controller.
If ignore, presenting other modal view will be in portrait mode:
The code that presenting the uiimagepickerController is as follow:
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.modalPresentationStyle = UIModalPresentationPopover;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
[self presentViewController:picker animated:YES completion:nil];
UIPopoverPresentationController *popupController = picker.popoverPresentationController;
if (popupController) {
popupController.barButtonItem = sender;
}
Any idea what have I done wrong, or it is a bug?
Whole example project can be downloaded here:
https://www.dropbox.com/s/zgipclyr0mz26c6/test.zip?dl=0
I have finally found the cause of my issue.
My app needs to support all orientation on iPad and Portrait mode only on iPhone. Therefore I added the following code of UIApplicationDelegate:
- (UIInterfaceOrientationMask) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if (window.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskPortrait;
}
But sometimes it gives me nil window, as in the case of UIImagePickerController presented using UIPopoverPresentationController on iPad, and will return UIInterfaceOrientationMaskPortrait and cause the status bar rotates to portrait mode. I have also noticed that this happens only when UIRequiresFullScreen is checked.
I have solved my issue by checking that window is not nil as below:
- (UIInterfaceOrientationMask) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if (window) {
if (window.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
return UIInterfaceOrientationMaskAll;
} else {
return UIInterfaceOrientationMaskPortrait;
}
} else {
return UIInterfaceOrientationMaskAll;
}
}
Related
I'm attempting to set a view's orientation based on if the device is running on an iPhone or an iPad. I have a supportedInterfaceOrientations method, which I call from viewDidLoad:
[self supportedInterfaceOrientations];
In the supportedInterfaceOrientations method, I check if the device is an iPhone. If it is, the view should only be set to a portrait orientation. Otherwise, all orientations should be supported:
- (NSUInteger)supportedInterfaceOrientations
{
//If the device is an iPhone, we're going to make this page portrait-only
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskPortrait;
}
//If it's an iPad, we'll support all orientations.
else {
return UIInterfaceOrientationMaskAll;
}
}
However, when I run the app on an iPhone, the view does not stay locked in portrait mode, but will rotate based based on the phone's orientation. Does anyone have an idea of what I'm doing wrong? Thank you!
EDIT I forgot to mention that this particular view is part of a Navigation Controller, which I just realized is probably the cause of the problem.
EDIT2 The xib for this particular view is also set to 'portrait' in the 'Simulated Metrics' section. Additionally, below is the code where I build the view controller and present the view:
self.navigationController = [[UINavigationController alloc] initWithRootViewController:self.mainViewController];
self.navigationController.delegate = self;
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
Use:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
//If the device is an iPhone, we're going to make this page portrait-only
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskPortrait;
}
//If it's an iPad, we'll support all orientations.
else {
return UIInterfaceOrientationMaskAll;
}
}
I have a universal application that supports both iPad and iPhone. On the iPad I support all orientations and on the iPhone, only Portrait.
I want one view controller (which is displayed modally), when running on the iPhone, to be displayed in any orientation that the device is in. I have seen many tutorials and SO posts that suggest using the -(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window app delegate method and this seems to work absolutely fine in respect to rotating the modal view controller. However, when I dismiss the modal view whilst in landscape, the entire app remains landscape.
Ideally, as soon as the modal view is dismissed the app should go back into portrait mode.
After investigation, it appears that the -(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window callback is not called after dismissing the view controller and I can't figure out why.
I have had a play with the example app here, and the callback is fired when dismissing the view controller, which I can't figure out as to why. The only difference I can see is that my view hierarchy is a lot more complex and that I'm displaying a Navigation Controller rather than an explicit view controller but I don't see how that should affect the callback.
Any ideas as to finding a solution to this?
Thanks
You can try to use this method (working in app store build):
[[UIDevice currentDevice] performSelector:#selector(setOrientation:)
withObject:(__bridge id)((void*)UIInterfaceOrientationPortrait)];
In appdelegate:
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
// Get topmost/visible view controller
UIViewController *currentViewController = [self topViewController];
//Hire check your needed device ore other things you need
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone )
{
//device is iphone
if ([currentViewController respondsToSelector:#selector(canRotate)])
{
// Unlock landscape view orientations for this view controller
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskPortrait;
}
else
//device Ipad
return UIInterfaceOrientationMaskAll;
}
}
- (UIViewController*)topViewController {
return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
} else if (rootViewController.presentedViewController) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
} else {
return rootViewController;
}
}
in the needed view to rotate make sure to implement
- (void)canRotate { }
This will allow you to rotate only the view you want for iPhone device.
I got this code with the help of an old stackoverflow post but i can`t find the link to it now
As I am working with iOS application support only Portrait Orientation.
But I am getting some Orientation related issue.
My app support only portrait orientation
when my parent P1.view is push using navigation it is in portrait mode thats fine. Now from P1.view I am subviewing my child view C1.view
Now I am in childview C1.view and from there using delegate I am calling
-(void)imagePickerController:(UIImagePickerController *)picker
[self.delegate openCamera];//called from C1.view
-(void)openCamera //declared in P1.view
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;>
[self presentModalViewController:picker animated:YES];
}
Now when I capture photo in Lanscape mode and dismiss that
presentModalViewController my view appears in Landscapmode instead of Portrait
mode.
- (BOOL)shouldAutorotate {
return YES;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return (interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
- (NSUInteger)supportedInterfaceOrientations {
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
This methods are define in both the view controller(P1 & C1) and also called when modelview dismiss, but my viewcontroller remains in Landscape which should not be happen.
This code working fine in iOS6 but not iOS7
I think that preferredInterfaceOrientationForPresentation is not supposed to return a bit mask. You could try:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
I am creating an app which is in landscape mode and I am using UIImagePickerController to take photos using iPhone camera in it and I want to create it in landscape mode too.
But as the Apple documention suggests UIImagePickerController does not support landscape orientation, so what should I do to get desired functionality?
If you'd like to use UIImagePickerController in landscape mode, use user1673099's answer, but instead of:
- (BOOL)shouldAutorotate
{
return NO;
}
use:
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscape;
}
and then the picker would open in landscape mode:
But make sure you check Portrait in deployment info:
... and I want to create it in landscape mode too.
One line of code can make a big difference! In the method or function where your IBAction lands:
In Swift,
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
// .overCurrentContext allows for landscape and portrait mode
imagePickerController.modalPresentationStyle = .overCurrentContext
Objective-C,
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
[imagePickerController setDelegate:self];
[imagePickerController setModalPresentationStyle: UIModalPresentationOverCurrentContext];
Note: This will allow imagePickerController to present it's view correctly, but will may not fix the issue of rotation while it is presented.
Try this way....
As per Apple Document, ImagePicker Controller never Rotate in Landscape mode. You have to use in Portrait Mode only.
For disable Landscape mode only for ImagePicker Controller follow below code:
In your ViewController.m:
Make the SubClass(NonRotatingUIImagePickerController) of Image Picker Controller
#interface NonRotatingUIImagePickerController : UIImagePickerController
#end
#implementation NonRotatingUIImagePickerController
// Disable Landscape mode.
- (BOOL)shouldAutorotate
{
return NO;
}
#end
Use as follow
UIImagePickerController* picker = [[NonRotatingUIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
etc.... Just as Default ImagePicker Controller
This is working for me & Let me know if you have any Problem.
This works great with Swift 4.0 in iOS 10/11.
import UIKit
extension UIImagePickerController {
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .all
}
}
Just drop the extension somewhere in your project, no need to subclass anything for it to work.
If you do need to specify device types, you can add a check like this:
import UIKit
extension UIImagePickerController {
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIDevice.current.userInterfaceIdiom == .phone ? .portrait : .all
}
}
This will allow an iPad to freely rotate, but enforces portrait mode on a phone. Just make sure that your app is configured to support these in its info.plist, otherwise you may encounter crashes upon launching the picker.
Here's a version that supports rotation in all interface orientations:
/// Not fully supported by Apple, but works as of iOS 11.
class RotatableUIImagePickerController: UIImagePickerController {
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .all
}
}
This way if the user rotates her device, it'll update the picker controller to support the current orientation. Just instantiate as you normally would a UIImagePickerController.
If you only want to support a subset of orientations, you can return a different value.
The correct way to use UIImagePickerController in landscape mode without any hacks is to put it into a UIPopoverController
- (void)showPicker:(id)sender
{
UIButton *button = (UIButton *)sender;
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
_popover = [[UIPopoverController alloc] initWithContentViewController:picker];
[_popover presentPopoverFromRect:button.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
Modify above code method
- (NSUInteger)supportedInterfaceOrientations
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationLandscapeRight || orientation == UIDeviceOrientationLandscapeLeft)
return UIInterfaceOrientationMaskLandscape;
else
return UIInterfaceOrientationMaskPortrait;
}
Accepted answer doesn't work for me. I had also to add modalPresentationStyle to UIImagePickerController to make it working.
UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
pickerController.modalPresentationStyle = UIModalPresentationCurrentContext; //this will allow the picker to be presented in landscape
pickerController.delegate = self;
pickerController.allowsEditing = YES;
pickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:pickerController animated:YES completion:nil];
And of course remember to put this in a controller that presents the picker:
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape; //this will force landscape
}
But according to Apple's documentation this is not supported to present this picker in the landscape mode so be careful about it.
If you're looking for SwiftUI solution in conjunction with the things mentioned here check this out here. Ignoring the safe area for the UIImagePickerController representable resolved a lot of my issues.
My application is working fine for iOS5. But for iOS6 I am getting the following orientation problem.
I have a ViewController VC1. when you rotate (change the orientation to UIInterfaceOrientationLandscapeRight) I want to present another ViewController VC2 and when you rotate back I need to dismiss VC2 and the VC1 should be in Portrait mode.
I am using tabBar in my application and I want this feature only for the first tab.
In tabBar I have written
-(BOOL) shouldAutorotate
{
UINavigationController *nav = (UINavigationController *)self.selectedViewController;
if ([nav.topViewController isKindOfClass:[MainViewController class]])
{
return YES;
}
else
{
return NO;
}
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
//Here I am writing code for presenting view(using notifications)
// but When I rotate the device to landscape it's getting called but when I rotate back
//to portrait I not getting called.
}
Thank you.
Please try this with ios 6 :(Example)
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait | UIInterfaceOrientationLandscapeRight | UIInterfaceOrientationLandscapeLeft;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}