Становится ли [UIScreen mainScreen] .bounds.size зависимым от ориентации в iOS8?

184

Я запустил следующий код в iOS 7 и iOS 8:

UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
BOOL landscape = (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight);
NSLog(@"Currently landscape: %@, width: %.2f, height: %.2f", 
      (landscape ? @"Yes" : @"No"), 
      [[UIScreen mainScreen] bounds].size.width, 
      [[UIScreen mainScreen] bounds].size.height);

Ниже приводится результат от iOS 8:

Currently landscape: No, width: 320.00, height: 568.00
Currently landscape: Yes, width: 568.00, height: 320.00

По сравнению с результатом в iOS 7:

Currently landscape: No, width: 320.00, height: 568.00
Currently landscape: Yes, width: 320.00, height: 568.00

Есть ли документация с указанием этого изменения? Или это временная ошибка в iOS 8 API?

lwxted
источник
13
Что ж, вывод iOS8 кажется гораздо более логичным
Леви

Ответы:

173

Да, это зависит от ориентации в iOS8, а не ошибка. Вы можете просмотреть сессию 214 WWDC 2014 для получения дополнительной информации: «Просмотр достижений контроллера в iOS 8»

Цитата из презентации:

UIScreen теперь ориентирован на интерфейс:

  • [UIScreen bounds] теперь ориентирован на интерфейс
  • [UIScreen applicationFrame] теперь ориентирован на интерфейс
  • Уведомления о рамке строки состояния ориентированы на интерфейс
  • Уведомления о кадре клавиатуры ориентированы на интерфейс
vhristoskov
источник
7
В упомянутой выше лекции WWDC часть, ориентированная на интерфейс, начинается в 51:50.
cnotethegr8
2
Я провел большую часть сегодняшнего дня, пытаясь обойти это продвижение. По-видимому, это не всегда происходит, поэтому, если вы разработчик SDK, который должен работать в других приложениях, этот материал станет еще сложнее.
Гленн Мейнард
1
@aroth, ты не можешь вводить новшества, не останавливая некоторые старые привычки.
Борис Янович
23
@borisy - я с уважением не согласен. В большинстве случаев возможно вводить новшества, сохраняя обратную совместимость. На мой взгляд, переломные изменения привкус лени больше всего на свете. Они хотят, чтобы все остальные делали эту работу.
Аромат
1
Этот ответ был бы еще более полезен, если бы было объяснено, что на самом деле означает ориентированный интерфейс . Я могу ошибаться, но я думаю, что зависимость от ориентации относится только к контроллеру представления. Если вы берете любой другой класс и вычисляете границы, они все равно соответствуют старому стилю, всегда портретному.
Макси Мус
59

Да, это зависит от ориентации в iOS8.

Я написал метод Util для решения этой проблемы для приложений, которые должны поддерживать более старые версии ОС.

+ (CGSize)screenSize {
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        return CGSizeMake(screenSize.height, screenSize.width);
    }
    return screenSize;
}
cbartel
источник
1
Я отредактировал код, пожалуйста, проверьте, ваше состояние для проверки версии ОС было неправильно
Jageen
@Jageen Пожалуйста, уточните, у меня работает проверка версий. В чем проблема?
cbartel
Я понимаю, что нам нужно изменить высоту и ширину, если ОС - iOS8, а ориентация - альбомная. Я прав?
Jageen
@Jageen Negative, в iOS 8 метод зависит от ориентации. Ваша логика переключена. Ваше изменение было правильно отклонено.
cbartel
@cbartel, есть ли способ классифицировать это в UIScreen, чтобы наш оригинальный вызов [UIScreen mainScreen] .bounds.size работал как для iOS 7, так и для iOS 8?
Кевинл
34

Да, действительно, размер экрана теперь зависит от ориентации в iOS 8. Иногда, однако, желательно получить размер, фиксированный для портретной ориентации. Вот как я это делаю.

+ (CGRect)screenBoundsFixedToPortraitOrientation {
    UIScreen *screen = [UIScreen mainScreen];

    if ([screen respondsToSelector:@selector(fixedCoordinateSpace)]) {
                    return [screen.coordinateSpace convertRect:screen.bounds toCoordinateSpace:screen.fixedCoordinateSpace];
    } 
    return screen.bounds;
}
MaxK
источник
2
Я обнаружил, что это решает проблему с обычными приложениями для iPhone, но не с приложениями для iPhone, работающими на iPad.
ThomasW
32

Да, теперь это зависит от ориентации.

Я предпочитаю приведенный ниже метод получения размера экрана независимо от ориентации некоторым ответам выше, потому что он проще и потому что он не зависит от кода ориентации (состояние которого может зависеть от время, когда они вызваны) или на проверку версии. Возможно, вы захотите новое поведение iOS 8, но это будет работать, если вам нужно, чтобы он был стабильным на всех версиях iOS.

+(CGSize)screenSizeOrientationIndependent {
     CGSize screenSize = [UIScreen mainScreen].bounds.size;
     return CGSizeMake(MIN(screenSize.width, screenSize.height), MAX(screenSize.width, screenSize.height));
}
mnemia
источник
Вероятно, не будет работать на чем-то вроде Apple TV, хотя, где он (почти) всегда шире, чем высокий.
cbh2000
11

В связи с этим вопросом, поскольку он решил мою проблему, вот два определения, которые я использую для расчета ширины и высоты экрана:

#define SCREEN_WIDTH (IOS_VERSION_LOWER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.width : [[UIScreen mainScreen] bounds].size.height) : [[UIScreen mainScreen] bounds].size.width)

#define SCREEN_HEIGHT (IOS_VERSION_LOWER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.height : [[UIScreen mainScreen] bounds].size.width) : [[UIScreen mainScreen] bounds].size.height)

#define IOS_VERSION_LOWER_THAN_8 (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)

Если вы поддерживаете iOS 7 и iOS 8, это лучшее решение для этой проблемы.

Antoine
источник
@Namit Gupta, ваше редактирование неверно. Если версия IOS 8 или выше, мы можем использовать размеры UIScreen, поскольку они зависят от ориентации. До iOS 8 вам приходилось проверять ориентацию строки состояния. Ваше редактирование теперь говорит о том, что все версии iOS не ниже 8 должны проверить статусную ориентацию, что неверно.
Антуан
9

Вы можете использовать nativeBounds(независимо от ориентации)

nativeBounds

Ограничительный прямоугольник физического экрана, измеряется в пикселях. (Только для чтения)

Декларация SWIFT

  var nativeBounds: CGRect { get }

Этот прямоугольник основан на устройстве в портретной ориентации. Это значение не изменяется при вращении устройства.

Определение высоты устройства:

if UIScreen.mainScreen().nativeBounds.height == 960.0 {

}

Определение ширины устройства:

if UIScreen.mainScreen().nativeBounds.width == 640.0 {

}
Лео Дабус
источник
3
Имейте в виду, что этот метод возвращает пиксели, в то время как большинство других методов работают с точками. Совсем разные вещи.
jcsahnwaldt восстановить Монику
1
В моем случае именно то, что мне нужно :) (OpenGL или другие виды, не основанные на UIViews, но где мне нужно знать точные размеры в пикселях)
Bersaelor
3
Предупреждение: на iphone 6+ это вернуло 1080 x 1920, что может быть не тем, что вы хотите
Chanon
8

Это не ошибка в iOS 8 SDK. Они сделали границы интерфейса зависимыми. В соответствии с вашим вопросом о какой-либо ссылке или документации на этот факт, я настоятельно рекомендую вам посмотреть, View Controller Advancements in iOS 8что это 214 сессия WWDC 2014 . Самая интересная часть (согласно вашим сомнениям) Screen Coordinatesначинается в 50:45.

Джулиан Крол
источник
5

Да, это зависит от ориентации в iOS8.

Вот как вы можете получить последовательный способ считывания границ в стиле iOS 8 для версий SDK и OS.

#ifndef NSFoundationVersionNumber_iOS_7_1
# define NSFoundationVersionNumber_iOS_7_1 1047.25
#endif

@implementation UIScreen (Legacy)

// iOS 8 way of returning bounds for all SDK's and OS-versions
- (CGRect)boundsRotatedWithStatusBar
{
    static BOOL isNotRotatedBySystem;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        BOOL OSIsBelowIOS8 = [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0;
        BOOL SDKIsBelowIOS8 = floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1;
        isNotRotatedBySystem = OSIsBelowIOS8 || SDKIsBelowIOS8;
    });

    BOOL needsToRotate = isNotRotatedBySystem && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
    if(needsToRotate)
    {
        CGRect screenBounds = [self bounds];
        CGRect bounds = screenBounds;
        bounds.size.width = screenBounds.size.height;
        bounds.size.height = screenBounds.size.width;
        return bounds;
    }
    else
    {
        return [self bounds];
    }
}

@end
hfossli
источник
1
Также рекомендуется проверить версию SDK, если коллеги создают ваше приложение с разными версиями SDK (хотя этого не должно быть!).
Крейг МакМахон,
4

Мое решение - это сочетание MaxK и hfossli. Я сделал этот метод на категории UIScreen, и у него нет проверок версий (что является плохой практикой):

//Always return the iOS8 way - i.e. height is the real orientation dependent height
+ (CGRect)screenBoundsOrientationDependent {
    UIScreen *screen = [UIScreen mainScreen];
    CGRect screenRect;
    if (![screen respondsToSelector:@selector(fixedCoordinateSpace)] && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        screenRect = CGRectMake(screen.bounds.origin.x, screen.bounds.origin.y, screen.bounds.size.height, screen.bounds.size.width);
    } else {
        screenRect = screen.bounds;
    }

    return screenRect;
}
Узаир Хан
источник
3

Мне нужна была быстрая вспомогательная функция, которая сохраняла бы то же поведение, что и iOS7 под iOS8 - это позволяло мне обмениваться [[UIScreen mainScreen] bounds]звонками и не трогать другой код ...

+ (CGRect)iOS7StyleScreenBounds {
    CGRect bounds = [UIScreen mainScreen].bounds;
    if (([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        bounds.size = CGSizeMake(bounds.size.height, bounds.size.width);
    }
        return bounds;
}
Джо Бут
источник
Неприятно, но выполняет работу до тех пор, пока не будет времени на исправление ситуации :-)
DuneCat
3

Приведенный ниже метод может использоваться для определения границ экрана для заданной ориентации, независимо от версии iOS. Этот метод возвращает границы в зависимости от размера экрана устройства и дает одинаковое значение CGRect независимо от версии iOS.

- (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation {

    CGFloat width   = [[UIScreen mainScreen] bounds].size.width;
    CGFloat height  = [[UIScreen mainScreen] bounds].size.height;

    CGRect bounds = CGRectZero;

    if (UIInterfaceOrientationIsLandscape(orientation)) {
        bounds.size = CGSizeMake(MAX(width, height), MIN(width, height));
    } else {
        bounds.size = CGSizeMake(MIN(width, height), MAX(width, height));
    }

    return bounds;
}

// For the below example, bounds will have the same value if you run the code on iOS 8.x or below versions.
CGRect bounds = [self boundsForOrientation:UIInterfaceOrientationPortrait]; 

источник
1

Вот что я использовал для вычисления правильного прямоугольника:

UIScreen* const mainScreen = [UIScreen mainScreen];
CGRect rect = [mainScreen bounds];
#ifdef __IPHONE_8_0
if ([mainScreen respondsToSelector:@selector(coordinateSpace)])
{
    if ([mainScreen respondsToSelector:@selector(fixedCoordinateSpace)])
    {
        id tmpCoordSpace = [mainScreen coordinateSpace];
        id tmpFixedCoordSpace = [mainScreen fixedCoordinateSpace];

        if ([tmpCoordSpace respondsToSelector:@selector(convertRect:toCoordinateSpace:)])
        {
            rect = [tmpCoordSpace convertRect:rect toCoordinateSpace: tmpFixedCoordSpace];
        }
    }
}
#endif
hhamm
источник
1

Просто добавив быструю версию превосходной функции cbartel, отвеченной выше.

func screenSize() -> CGSize {
    let screenSize = UIScreen.mainScreen().bounds.size
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
        return CGSizeMake(screenSize.height, screenSize.width)
    }
    return screenSize
}
Мартин Колес
источник
1

Моя проблема была связана с рамкой UIWindows, которая собиралась в минус. Так сделал код как показано ниже в MyViewController - (NSUInteger)

[[UIApplication sharedApplication] setStatusBarHidden:NO];

[self.view setFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height)];

[appDel.window setFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height)];

И его работа для меня, попробуйте.

Хардик Мамтора
источник
1

iOS 8 или выше

Решение для тех, кто хочет узнать размер экрана в пунктах (3,5-дюймовый экран имеет 320 × 480 точек, 4,0-дюймовый экран имеет 320 × 568 точек и т. Д.)

- (CGSize)screenSizeInPoints
{
    CGFloat width = [[UIScreen mainScreen] bounds].size.width;
    CGFloat height = [[UIScreen mainScreen] bounds].size.height;

    if (width > height) {
        return CGSizeMake(height, width);
    }
    else {
        return [[UIScreen mainScreen] bounds].size;
    }
}
Стефан
источник
0

Одна вещь, которую я заметил, это то, что порядок поддерживаемых ориентаций интерфейса в Info.plist имеет значение. У меня возникла проблема этого вопроса с моим приложением (которое выполняет ориентацию в коде), но я нигде не указал, что ориентация по умолчанию - Портрет.

Я думал, что ориентация по умолчанию была Портретом в любом случае.

Перестановка itens в Info.plist, в первую очередь Portrait, восстановила ожидаемое поведение.

EPX
источник
0

Это даст правильное устройство в iOS7 и iOS8 ,

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define IS_PORTRAIT         UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation)

+ (BOOL)isIPHONE4{

// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

        if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 480.0) {
            return YES;
        } else {
            return NO;
        }

// >= iOS 8.0
}else{

    if(IS_PORTRAIT){

        if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 480.0) {
            return YES;
        } else {
            return NO;
        }

    }else{

        if ([self getDeviceWidth] == 480.0 && [self getDeviceHeight] == 320.0) {
            return YES;
        } else {
            return NO;
        }

    }

}


}

+ (BOOL)isIPHONE5{


// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

    if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 568.0) {
        return YES;
    } else {
        return NO;
    }

    // >= iOS 8.0
}else{

    if(IS_PORTRAIT){

        if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 568.0) {
            return YES;
        } else {
            return NO;
        }

    }else{

        if ([self getDeviceWidth] == 568.0 && [self getDeviceHeight] == 320.0) {
            return YES;
        } else {
            return NO;
        }

    }

}

}

+ (BOOL)isIPHONE6{

// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

    if ([self getDeviceWidth] == 375.0 && [self getDeviceHeight] == 667.0) {
        return YES;
    } else {
        return NO;
    }

    // >= iOS 8.0
}else{

    if(IS_PORTRAIT){

        if ([self getDeviceWidth] == 375.0 && [self getDeviceHeight] == 667.0) {
            return YES;
        } else {
            return NO;
        }

    }else{

        if ([self getDeviceWidth] == 667.0 && [self getDeviceHeight] == 375.0) {
            return YES;
        } else {
            return NO;
        }

    }

}


}
+ (BOOL)isIPHONE6Plus{


// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0")){

    if ([self getDeviceWidth] == 414.0 && [self getDeviceHeight] == 736.0) {
        return YES;
    } else {
        return NO;
    }

    // >= iOS 8.0
}else{

    if(IS_PORTRAIT){

        if ([self getDeviceWidth] == 414.0 && [self getDeviceHeight] == 736.0) {
            return YES;
        } else {
            return NO;
        }

    }else{

        if ([self getDeviceWidth] == 736.0 && [self getDeviceHeight] == 414.0) {
            return YES;
        } else {
            return NO;
        }

    }

}


}

+ (CGFloat)getDeviceHeight{

//NSLog(@"Device width: %f",[UIScreen mainScreen].bounds.size.height);
return [UIScreen mainScreen].bounds.size.height;
}
+ (CGFloat)getDeviceWidth{

//NSLog(@"Device width: %f",[UIScreen mainScreen].bounds.size.height);
return [UIScreen mainScreen].bounds.size.width;
}

// Вы также можете добавить больше устройств (ieiPad).

Мухаммед Заид Патан
источник
Вопрос не в распознавании устройства по разрешению экрана. Более того, Apple рекомендует создавать адаптивный интерфейс, основанный на классах размеров, а не на устройстве / экране обнаружения
Джулиан Крол
0

Использовал слегка модифицированное решение mnemia, которое без проверки версии iOS, используя min / max на границах главного экрана.
Мне нужно CGRectтак получилCGRect от границ и В начале изменились size.width=min(w,h), size.height=max(w,h). Затем я вызвал эту независимую от ОС CGRectфункцию get в двух местах в моем коде, где я получал размер экрана OpenGL, касался и т. Д. До исправления у меня было 2 проблемы - на IOS 8.x в ландшафтном режиме положение OpenGLотображения было неверным: 1 / 4 из полного экрана в левой нижней части. И второе касание вернуло недопустимые значения. Обе проблемы были исправлены, как объяснено. Спасибо!

NoAngel
источник