问题
In my universal app I currently override supportedInterfaceOrientations
in the window's root view controller to define the orientations that are allowed. Up until now the decision was based on the device's user interface idiom:
- (NSUInteger) supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
else
return UIInterfaceOrientationMaskAll;
}
Now I would like to change this so that I can also support landscape for the iPhone 6 Plus, but NOT for other iPhones. I can image one or two solutions, but these are all rather brittle and will probably break when Apple starts to make new devices.
In an ideal world I would like to change the above method to look like the following snippet, where the decision is based on the device's user interface size class instead of the user interface idiom:
- (NSUInteger) supportedInterfaceOrientations
{
// Note the hypothetical UIDevice method "landscapeSizeClass"
if ([[UIDevice currentDevice] landscapeSizeClass] == UIUserInterfaceSizeClassCompact)
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
else
return UIInterfaceOrientationMaskAll;
}
Is there something like this magical landscapeSizeClass
method somewhere in UIKit? I have looked around a bit in various class references and guides, but didn't find anything useful. Or can someone suggest a different solution that is similarly generic and future-proof?
Note that my app creates its UI programmatically, so purely storyboard-based solutions are out. Also my app still needs to support iOS 7 so I can't just change everything to use size classes. What I can do, though, is to make runtime checks before I use simple iOS 8 APIs.
回答1:
I will rather use macros to discover that (for readability reasons), with something like:
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
#define SCREEN_MAX_LENGTH (MAX(SCREEN_WIDTH, SCREEN_HEIGHT))
#define SCREEN_MIN_LENGTH (MIN(SCREEN_WIDTH, SCREEN_HEIGHT))
#define IS_IPHONE_6P (IS_IPHONE && SCREEN_MAX_LENGTH == 736.0)
And then:
- (NSUInteger) supportedInterfaceOrientations
{
if (IS_IPAD || IS_IPHONE_6P)
{
return UIInterfaceOrientationMaskAll;
}
else
{
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
}
Edited:
It may better to have this one macro instead IS_IPHONE_6P and IS_IPAD:
#define IS_LANDSCAPE_REGULAR_SIZE (SCREEN_MAX_LENGTH >= 736.0)
And then:
if (IS_LANDSCAPE_REGULAR_SIZE) { ... }
回答2:
Lacking an official Apple API, this is the workaround that I've come up with:
- (NSUInteger) supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
// iPhone 5S and below: 320x480
// iPhone 6: 375x667
// iPhone 6 Plus: 414x736
CGSize screenSize = [UIScreen mainScreen].bounds.size;
// The way how UIScreen reports its bounds has changed in iOS 8.
// Using MIN() and MAX() makes this code work for all iOS versions.
CGFloat smallerDimension = MIN(screenSize.width, screenSize.height);
CGFloat largerDimension = MAX(screenSize.width, screenSize.height);
if (smallerDimension >= 400 && largerDimension >= 700)
return UIInterfaceOrientationMaskAll;
else
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
else
{
// Don't need to examine screen dimensions on iPad
return UIInterfaceOrientationMaskAll;
}
}
The snippet simply assumes that a screen with dimensions above a semi-arbitrarily chosen size is suitable for rotation. Semi-arbitrarily, because a threshold of 400x700 includes the iPhone 6 Plus, but excludes the iPhone 6.
Although this solution is rather simple, I like it exactly because of its lack of sophistication. I don't really need to distinguish exactly between devices, so any clever solutions such as the one in Jef's answer are overkill for my purposes.
回答3:
- (NSUInteger) supportedInterfaceOrientations {
if (self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassRegular ||
self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
return UIInterfaceOrientationMaskAll;
} else {
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
}
Update: Previous is not working so here is different solution
- (NSUInteger) supportedInterfaceOrientations {
if (self.traitCollection.displayScale == 3.0 || (self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassRegular &&
self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular)) {
return UIInterfaceOrientationMaskAll;
} else {
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
}
回答4:
Fwiw, the equivalent of herzbube's answer in Swift 1.2:
override func supportedInterfaceOrientations() -> Int {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
let size = self.view.bounds.size
let smallerDimension = min(size.width, size.height)
let largerDimension = max(size.width, size.height)
if smallerDimension >= 400 && largerDimension >= 700 {
return Int(UIInterfaceOrientationMask.All.rawValue)
} else {
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
} else {
return Int(UIInterfaceOrientationMask.All.rawValue)
}
}
though personally I prefer the following style:
override func supportedInterfaceOrientations() -> Int {
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
return Int(UIInterfaceOrientationMask.All.rawValue)
}
let size = self.view.bounds.size
let smallerDimension = min(size.width, size.height)
let largerDimension = max(size.width, size.height)
return smallerDimension >= 400 && largerDimension >= 700 ?
Int(UIInterfaceOrientationMask.All.rawValue) :
Int(UIInterfaceOrientationMask.Portrait.rawValue)
}
来源:https://stackoverflow.com/questions/28529012/allow-landscape-for-iphone-6-plus-but-not-other-iphones