Как получить высоту и ширину экрана в зависимости от ориентации?

96

Я пытаюсь программно определить текущую высоту и ширину моего приложения. Я использую это:

CGRect screenRect = [[UIScreen mainScreen] bounds];

Но это дает ширину 320 и высоту 480, независимо от того, находится ли устройство в портретной или альбомной ориентации. Как я могу определить текущую ширину и высоту (т.е. в зависимости от ориентации устройства) моего главного экрана?

MusiGenesis
источник

Ответы:

164

Вы можете использовать что-то вроде UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)определения ориентации, а затем соответственно использовать размеры.

ОДНАКО, во время изменения ориентации, как в UIViewController's

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
                                 duration:(NSTimeInterval)duration

Используйте переданную ориентацию, toInterfaceOrientationпоскольку statusBarOrientation UIApplication по-прежнему будет указывать на старую ориентацию, поскольку она еще не изменилась (поскольку вы находитесь внутри willобработчика событий).

Резюме

По этому поводу есть несколько связанных сообщений, но каждая из них, похоже, указывает на то, что вам необходимо:

  1. Посмотрите, [[UIScreen mainScreen] bounds]чтобы получить размеры,
  2. Проверьте, в какой вы ориентации, и
  3. Учитывайте высоту строки состояния (если отображается)

Ссылки

Рабочий код

Я обычно не захожу так далеко, но вы пробудили мой интерес. Следующий код должен помочь. Я написал категорию по UIApplication. Я добавил методы класса для получения currentSize или размера в заданной ориентации, что вы бы назвали в UIViewController willRotateToInterfaceOrientation:duration:.

@interface UIApplication (AppDimensions)
+(CGSize) currentSize;
+(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation;
@end

@implementation UIApplication (AppDimensions)

+(CGSize) currentSize
{
    return [UIApplication sizeInOrientation:[UIApplication sharedApplication].statusBarOrientation];
}

+(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation
{
    CGSize size = [UIScreen mainScreen].bounds.size;
    UIApplication *application = [UIApplication sharedApplication];
    if (UIInterfaceOrientationIsLandscape(orientation))
    {
        size = CGSizeMake(size.height, size.width);
    }
    if (application.statusBarHidden == NO)
    {
        size.height -= MIN(application.statusBarFrame.size.width, application.statusBarFrame.size.height);
    }
    return size;
}

@end

Использовать код простой вызов [UIApplication currentSize]. Кроме того, я запустил приведенный выше код, поэтому я знаю, что он работает и сообщает правильные ответы во всех ориентациях. Обратите внимание, что я учитываю строку состояния. Интересно, что мне пришлось вычесть MIN из высоты и ширины строки состояния.

Надеюсь это поможет. : D

Другие мысли

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

(gdb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] представление]

<UILayoutContainerView: 0xf7296f0; кадр = (0 0; 320 480); преобразование = [0, -1, 1, 0, 0, 0]; авторазмер = W + H; слой = <CALayer: 0xf729b80 >>

(gdb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] представление]

<UILayoutContainerView: 0xf7296f0; кадр = (0 0; 320 480); авторазмер = W + H; слой = <CALayer: 0xf729b80 >>

Не уверен, как работает ваше приложение, но если вы не используете какой-либо контроллер навигации, у вас может быть UIView под основным видом с максимальной высотой / шириной родительского элемента и увеличивается / уменьшается вместе с родительским элементом. Тогда вы могли бы сделать: [[[[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] subviews] objectAtIndex:0] frame]. В одной строке это выглядит довольно интенсивно, но вы поняли идею.

Однако ... Все же было бы лучше выполнить указанные выше 3 шага под резюме. Начните возиться с UIWindows, и вы обнаружите странные вещи, например, показ UIAlertView изменит ключевое окно UIApplication так, чтобы он указывал на новый UIWindow, созданный UIAlertView. Кто знал? Я сделал это после того, как обнаружил ошибку, полагающуюся на keyWindow, и обнаружил, что она так изменилась!

Сэм
источник
5
Это кажется такой элементарной задачей, что я не могу поверить, что мне нужно взломать свой собственный код, чтобы определить что-то вроде этого.
MusiGenesis
2
Я попробую другое решение и сделаю репост, если оно сработает. Этот сайт настолько агрессивен, что если вы не отвечаете быстро, то можете вообще не отвечать. ха-ха ...: D Надеюсь, мой ответ хотя бы полезен. Опять же, я попробую что-нибудь еще очень быстро.
Сэм
1
Большой! (Кстати, у каждого есть интерес) :-)
Vamos
1
Если вы используете applicationFrameвместо bounds(на UIScreen), вам не нужно вычитать высоту строки состояния.
aopsfan
2
stackoverflow.com/questions/24150359/… mainScreen().bounds.size стал зависимым от ориентации, начиная с iOS 8,
Джеральд
39

Это мой код решения! Этот метод можно добавить к классу Category NSObject, или вы можете определить верхний настраиваемый класс UIViewController и позволить всем другим вашим UIViewController унаследовать его.

-(CGRect)currentScreenBoundsDependOnOrientation
{  

    CGRect screenBounds = [UIScreen mainScreen].bounds ;
    CGFloat width = CGRectGetWidth(screenBounds)  ;
    CGFloat height = CGRectGetHeight(screenBounds) ;
    UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;

    if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
        screenBounds.size = CGSizeMake(width, height);
    }else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
        screenBounds.size = CGSizeMake(height, width);
    }
    return screenBounds ;
}

Обратите внимание , что после IOS8, как говорится в Apple Document из свойства границ UIScreen :

Обсуждение

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

поэтому для рассмотрения совместимости мы должны определить версию IOS и внести изменения, как показано ниже:

#define IsIOS8 (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1)

-(CGRect)currentScreenBoundsDependOnOrientation
{  

    CGRect screenBounds = [UIScreen mainScreen].bounds ;
    if(IsIOS8){
        return screenBounds ;
    }
    CGFloat width = CGRectGetWidth(screenBounds)  ;
    CGFloat height = CGRectGetHeight(screenBounds) ;
    UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;

    if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
        screenBounds.size = CGSizeMake(width, height);
    }else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
        screenBounds.size = CGSizeMake(height, width);
    }
    return screenBounds ;
}
Monjer
источник
Практически то же самое, что и мой ответ, единственное, что он упускает, - это перевернутая портретная ориентация. Это имеет значение только в том случае, если вы поддерживаете эту ориентацию.
Роберт Вагстафф,
2
@Monjer, вы не должны называть методы, которые не выполняют GET-запрос, с префиксом i со словом get. currentScreenBoundsDependOnOrientation - лучшее название для метода
bogen
1
@ Hakonbogen.yes, возможно, вы правы, потому что объявление peroperty автоматически генерирует метод установки / получения, и это может привести к конфликту имен и противоречить соглашениям об именах objc. Спасибо за ваш совет.
monjer
Приятно, что Apple наконец-то молчаливо признала, что их код вращения был полным беспорядком, и просто начал говорить нам, какие чертовы размеры в текущей ориентации. Жаль, что для этого понадобилась версия 8.
MusiGenesis
30

Вот удобный макрос:

#define SCREEN_WIDTH (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.width : [[UIScreen mainScreen] bounds].size.height)
#define SCREEN_HEIGHT (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.height : [[UIScreen mainScreen] bounds].size.width)
Роберт Вагстафф
источник
14

В iOS 8+ вы должны использовать viewWillTransitionToSize:withTransitionCoordinatorметод:

-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    // You can store size in an instance variable for later
    currentSize = size;

    // This is basically an animation block
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Get the new orientation if you want
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

        // Adjust your views
        [self.myView setFrame:CGRectMake(0, 0, size.width, size.height)];

    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        // Anything else you need to do at the end
    }];
}

Это заменяет устаревший метод анимации, который не давал информации о размере:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
Марк Хеннингс
источник
Это должен быть принятый ответ. Современный и актуальный.
SarpErdag
Используйте этот ответ для iOS 8 и выше.
iPhoneDeveloper
10

В iOS 8 границы экрана теперь возвращаются правильно для текущей ориентации. Это означает, что iPad в альбомной ориентации [UIScreen mainScreen] .bounds вернет 768 на iOS <= 7 и 1024 на iOS 8.

Следующее возвращает правильную высоту и ширину для всех выпущенных версий.

-(CGRect)currentScreenBoundsDependOnOrientation
{
    NSString *reqSysVer = @"8.0";
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
        return [UIScreen mainScreen].bounds;

    CGRect screenBounds = [UIScreen mainScreen].bounds ;
    CGFloat width = CGRectGetWidth(screenBounds)  ;
    CGFloat height = CGRectGetHeight(screenBounds) ;
    UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;

    if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
        screenBounds.size = CGSizeMake(width, height);
        NSLog(@"Portrait Height: %f", screenBounds.size.height);
    }else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
        screenBounds.size = CGSizeMake(height, width);
        NSLog(@"Landscape Height: %f", screenBounds.size.height);
    }

    return screenBounds ;
}
А.Баджер
источник
8

если вам нужен размер, зависящий от ориентации, и у вас есть представление, вы можете просто использовать:

view.bounds.size
Tod
источник
ЗДОРОВО! Решение KISS = Keep It Simple and Stupid
bsorrentino
3
KISS не означает «Будь простым и глупым» - LOL! Это означает «Будь простым, глупым!» :-)
Эрик ван дер Нейт
Кроме того, этот ответ, очевидно, работает только в том случае, если известно, что ваше представление точно полноэкранное. Но если это так, то у вас, вероятно, нет исходной проблемы, опубликованной OP.
Эрик ван дер Нейт
5

Я написал категорию UIScreen, которая работает на всех версиях IOS, так что вы можете использовать его как это:
[[UIScreen mainScreen] currentScreenSize].

@implementation UIScreen (ScreenSize)

- (CGSize)currentScreenSize {
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
    CGSize screenSize = screenBounds.size;

    if ( NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1 ) {  
        UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        if ( UIInterfaceOrientationIsLandscape(interfaceOrientation) ) {
            screenSize = CGSizeMake(screenSize.height, screenSize.width);
        }
    }

    return screenSize;
}

@end
ИскусствоFeel
источник
1
Мне кажется, это самый ясный ответ. Проголосовал "за".
Эрик ван дер Нейт
5

Вот быстрый способ получить размеры экрана в зависимости от ориентации:

var screenWidth: CGFloat {
    if UIInterfaceOrientationIsPortrait(screenOrientation) {
        return UIScreen.mainScreen().bounds.size.width
    } else {
        return UIScreen.mainScreen().bounds.size.height
    }
}
var screenHeight: CGFloat {
    if UIInterfaceOrientationIsPortrait(screenOrientation) {
        return UIScreen.mainScreen().bounds.size.height
    } else {
        return UIScreen.mainScreen().bounds.size.width
    }
}
var screenOrientation: UIInterfaceOrientation {
    return UIApplication.sharedApplication().statusBarOrientation
}

Они включены как стандартная функция в мой проект:

https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
источник
0
float msWidth = [[UIScreen mainScreen] bounds].size.width*(IS_RETINA?2.0f:1.0f);
float msHeight = [[UIScreen mainScreen] bounds].size.height*(IS_RETINA?2.0f:1.0f);
if ( UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ) {
    os->setWidth(MIN(msWidth, msHeight));
    os->setHeight(MAX(msWidth, msHeight));
} else {
    os->setWidth(MAX(msWidth, msHeight));
    os->setHeight(MIN(msWidth, msHeight));
}

NSLog(@"screen_w %f", os->getWidth());
NSLog(@"screen_h %f", os->getHeight());
user4082558
источник
0

Однако в iOS 8.0.2:

+ (NSUInteger)currentWindowWidth
{
    NSInteger width = 0;
    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
    CGSize size = [UIScreen mainScreen].bounds.size;
   // if (UIInterfaceOrientationIsLandscape(orientation)) {
   //     width = size.height;
   // } else {
        width = size.width;
  //  }

    return width;
}
Ганк
источник
0

используйте -> setNeedsDisplay () для представления, размер которого вы хотите изменить.

Сантош
источник