Чи стає розмір [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

Чи є документація, яка вказує цю зміну? Або це тимчасова помилка в API iOS 8?


13
Ну а вихід iOS8 здається набагато логічнішим
Леві

Відповіді:


173

Так, це залежить від орієнтації в iOS8, а не помилка. Ви можете переглянути сеанс 214 від WWDC 2014 для отримання додаткової інформації: "Переглянути вдосконалення контролера в iOS 8"

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

Тепер UIScreen орієнтований на інтерфейс:

  • [Межі UIScreen] тепер орієнтовані на інтерфейс
  • [UIScreen applicationFrame] тепер орієнтований на інтерфейс
  • Повідомлення кадру рядка стану орієнтовані на інтерфейс
  • Повідомлення кадру клавіатури орієнтовані на інтерфейс

7
У згаданій вище лекції WWDC орієнтована на інтерфейс частина починається з 51:50.
cnotethegr8

2
Я витратив більшу частину сьогодні, намагаючись обійти цей прогрес. Мабуть, це не завжди буває, тому якщо ви розробник SDK, який потребує роботи в інших додатках, цей матеріал просто ускладнився.
Гленн Мейнард

1
@aroth ви не можете внести інновації без гальмування старих звичок.
Борис Юрійович

23
@borisy - я з повагою не згоден. У більшості випадків можливі інновації, зберігаючи зворотну сумісність. Порушення змін присмакує лінь більше, ніж будь-що інше, на мій погляд. Вони хочуть, щоб усі інші виконували роботу.
aroth

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;
}

1
Я відредагував код, будь ласка, перевірте, ваш стан перевірки версії ОС був неправильним
Jageen

@Jageen Поясніть, будь ласка, перевірка версії працює для мене. В чому проблема?
cbartel

Що я розумію, нам потрібно змінити висоту і ширину, якщо ОС iOS8, а орієнтація - Пейзаж, я прав?
Jageen

@Jageen Negative, в iOS 8 метод залежить від орієнтації. Ваша логіка переключена. Вашу редакцію було відхилено правильно.
cbartel

@cbartel Чи є спосіб класифікувати це на UIScreen, щоб наш оригінальний дзвінок на [UIScreen mainScreen] .bounds.size працював як для iOS 7, так і для iOS 8?
kevinl

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;
}

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));
}

Напевно, не буде працювати на чомусь схожому на 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, це найкраще рішення цієї проблеми.


@Namit Gupta, ваше редагування неправильне. Якщо версія IOS становить 8 або вище, ми можемо використовувати розміри UIScreen, оскільки вони залежать від орієнтації. Перед iOS 8 вам потрібно було перевірити орієнтацію на панелі стану. Ваша редакція тепер говорить про те, що всі версії iOS не нижче 8 повинні перевіряти статус-орієнтування, що неправильно.
Антуан

9

Ви можете використовувати nativeBounds(незалежно від орієнтації)

рідні зв’язки

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

Декларація SWIFT

  var nativeBounds: CGRect { get }

Цей прямокутник заснований на пристрої в портретній орієнтації. Це значення не змінюється під час обертання пристрою.

Визначення висоти пристрою:

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

}

Визначення ширини пристрою:

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

}

3
Майте на увазі, що цей метод повертає пікселі, тоді як більшість інших методів мають справу з пунктами. Дуже різні речі.
jcsahnwaldt Reinstate Monica

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

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

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, що підтримуєтьсяInterfaceOrientations

[[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, поклавши портрет на перше місце, відновили очікувану поведінку.


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;
}

// Ви також можете додати більше пристроїв (тобто iPad).


Питання не в розпізнаванні пристрою на основі роздільної здатності екрана. Крім того, Apple рекомендує створити адаптивний інтерфейс, який базується на класах розмірів, а не на виявленні пристрою / екрану
Julian Król

0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.