O [UIScreen mainScreen] .bounds.size está se tornando dependente da orientação no iOS8?

184

Executei o seguinte código no iOS 7 e 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);

A seguir, o resultado do iOS 8:

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

Comparando com o resultado no iOS 7:

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

Existe alguma documentação especificando essa alteração? Ou é um bug temporário nas APIs do iOS 8?

lwxted
fonte
13
Bem a saída iOS8 parece muito mais lógico
Levi

Respostas:

173

Sim, é dependente da orientação no iOS8, não um bug. Você pode revisar a sessão 214 da WWDC 2014 para obter mais informações: "Exibir avanços do controlador no iOS 8"

Citação da apresentação:

O UIScreen agora é orientado por interface:

  • [Limites UIScreen] agora orientado a interface
  • [UIScreen applicationFrame] agora orientado a interface
  • As notificações do quadro da barra de status são orientadas à interface
  • As notificações do quadro do teclado são orientadas à interface
vhristoskov
fonte
7
Na palestra da WWDC mencionada acima, a parte orientada à interface começa às 51:50.
cnotethegr8
2
Passei a maior parte de hoje tentando contornar esse avanço. Aparentemente, isso nem sempre acontece; portanto, se você é um desenvolvedor de um SDK que precisa trabalhar em outros aplicativos, essas coisas ficam ainda mais complicadas.
Glenn Maynard
1
@ você não pode inovar sem frear alguns hábitos antigos.
Boris Y.
23
@borisy - eu discordo respeitosamente. Na maioria dos casos, é possível inovar, preservando a compatibilidade com versões anteriores. Quebrar mudanças cheira a preguiça mais do que qualquer outra coisa, na minha opinião. Eles querem que todo mundo faça o trabalho.
Aroth
1
Essa resposta seria ainda mais útil se fosse explicado o que realmente significa orientação para interface . Posso estar errado, mas acho que a dependência da orientação refere-se apenas à exibição de controladores. Se você fizer qualquer outra aula e calcular os limites, eles ainda estarão de acordo com o estilo antigo, sempre em retrato.
Maxi Mus
59

Sim, depende da orientação no iOS8.

Eu escrevi um método Util para resolver esse problema para aplicativos que precisam suportar versões mais antigas do sistema operacional.

+ (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
fonte
1
Eu editei o código de verificação por favor, a sua condição de verificação de versão OS estava errado
Jageen
@Jageen Por favor, esclareça que a verificação da versão funciona para mim. Qual é o problema?
cbartel
O que eu entendo é que precisamos alterar a altura e a largura se o SO for iOS8 e a orientação for Paisagem, estou certo?
Jageen 03/09
@Jageen Negative, no iOS 8, o método depende da orientação. Sua lógica está alterada. Sua edição foi rejeitada corretamente.
Cbartel 03/09
@cbartel existe uma maneira de categorizar isso em UIScreen para que nossa chamada original para [UIScreen mainScreen] .bounds.size funcione para iOS 7 e iOS 8?
kevinl
34

Sim, na verdade, o tamanho da tela agora depende da orientação no iOS 8. Às vezes, porém, é desejável obter um tamanho fixo para a orientação retrato. Aqui está como eu faço isso.

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

    if ([screen respondsToSelector:@selector(fixedCoordinateSpace)]) {
                    return [screen.coordinateSpace convertRect:screen.bounds toCoordinateSpace:screen.fixedCoordinateSpace];
    } 
    return screen.bounds;
}
MaxK
fonte
2
Estou descobrindo que isso resolve o problema com aplicativos regulares para iPhone, mas não com aplicativos para iPhone em execução no iPad.
precisa saber é o seguinte
32

Sim, agora depende da orientação.

Prefiro o método abaixo para obter o tamanho da tela de maneira independente da orientação do que algumas das respostas acima, tanto porque é mais simples quanto porque não depende de nenhum código de orientação (cujo estado pode depender do quando são chamados) ou na verificação de versão. Você pode querer o novo comportamento do iOS 8, mas isso funcionará se você precisar ser estável em todas as versões do iOS.

+(CGSize)screenSizeOrientationIndependent {
     CGSize screenSize = [UIScreen mainScreen].bounds.size;
     return CGSizeMake(MIN(screenSize.width, screenSize.height), MAX(screenSize.width, screenSize.height));
}
mnemia
fonte
Provavelmente não funcionará em algo como o Apple TV, onde é (quase) sempre mais largo do que alto.
cbh2000
11

Relacionado a esta pergunta, pois resolveu meu problema, aqui duas definem que eu uso para cálculos de largura e altura da tela:

#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)

Se você suporta o iOS 7 e o iOS 8, esta é a melhor solução para esse problema.

Antoine
fonte
@Namit Gupta, sua edição está errada. Se a versão do IOS for 8 ou superior, podemos usar as dimensões do UIScreen, pois elas dependem da orientação. Antes do iOS 8, era necessário verificar a orientação da barra de status. Sua edição agora está dizendo que todas as versões do iOS não inferiores a 8 devem verificar a orientação da barra de status, o que está errado.
Antoine
9

Você pode usar nativeBounds(independente da orientação)

nativeBounds

O retângulo delimitador da tela física, medido em pixels. (somente leitura)

Declaração SWIFT

  var nativeBounds: CGRect { get }

Esse retângulo é baseado no dispositivo em uma orientação de retrato para cima. Este valor não muda à medida que o dispositivo gira.

Detectando a altura do dispositivo:

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

}

Detectando a largura do dispositivo:

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

}
Leo Dabus
fonte
3
Esteja ciente de que esse método retorna pixels, enquanto a maioria dos outros métodos lida com pontos. Coisas muito diferentes.
Jcsahnwaldt Restabelecer Monica
1
No meu caso exatamente o que eu precisava :) (OpenGL ou outros pontos de vista não baseado em UIViews mas onde eu preciso saber as dimensões de pixel exatas)
Bersaelor
3
Aviso: no iphone 6+ este retornou 1080 x 1920 que pode não ser o que você quer
Chanon
8

Não é um bug no iOS 8 SDK. Eles estabeleceram limites na orientação da interface. De acordo com sua pergunta sobre alguma referência ou documentação para esse fato, eu recomendo que você assista à View Controller Advancements in iOS 8214 sessão da WWDC 2014 . A parte mais interessante (de acordo com suas dúvidas) é a Screen Coordinatesque começa às 50:45.

Julian Król
fonte
5

Sim, depende da orientação no iOS8.

Veja como você pode ter uma maneira consistente de ler os limites do iOS 8 nas versões do SDK e do SO.

#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
fonte
1
Verificar a versão do SDK também é uma boa dica, caso os colegas estejam criando seu aplicativo com versões diferentes do SDK (embora isso não deva estar acontecendo!).
Craig McMahon
4

Minha solução é uma combinação de MaxK e hfossli. Eu fiz esse método em uma categoria do UIScreen e ele não possui verificações de versão (o que é uma prática ruim):

//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;
}
Uzair Khan
fonte
3

Eu precisava de uma função de ajuda rápida que mantivesse o mesmo comportamento do iOS7 no iOS8 - isso me permitia trocar minhas [[UIScreen mainScreen] bounds]chamadas e não tocar em outro código ...

+ (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;
}
Joe Booth
fonte
Desagradável, mas começa o trabalho feito até ao momento pode ser colocado em consertar as coisas adequadamente :-)
DuneCat
3

O método abaixo pode ser usado para encontrar os limites da tela para uma determinada orientação, independentemente da versão do iOS. Este método retornará os limites com base no tamanho da tela do dispositivo e fornecerá o mesmo valor de CGRect independente da versão do 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]; 

fonte
1

Isso é o que eu usei para calcular o correto correto:

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
fonte
1

Apenas adicionando a versão rápida de uma excelente função cbartel, respondida acima.

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
}
Martin Koles
fonte
1

Meu problema estava relacionado ao quadro UIWindows, que estava diminuindo. Por isso, criei o código abaixo no MyViewController - (NSUInteger) Método supportedInterfaceOrientations

[[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)];

E seu trabalho para mim, tente.

Hardik Mamtora
fonte
1

iOS 8 ou superior

Uma solução para quem deseja descobrir o tamanho da tela em pontos (a tela de 3,5 polegadas possui 320 × 480 pontos, a tela de 4,0 polegadas possui 320 × 568 pontos etc.) seria

- (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;
    }
}
Stefan
fonte
0

Uma coisa que observei é que a ordem das orientações de interface suportadas no Info.plist é importante. Eu obtive o problema desta pergunta com meu aplicativo (que faz orientação no código), mas não especifiquei em nenhum lugar que a orientação padrão seja Portrait.

Eu pensei que a orientação padrão era Retrato em qualquer caso.

Reorganizar itens no Info.plist, colocando o Portrait em primeiro lugar, restaurou o comportamento esperado.

epx
fonte
0

Isso fornecerá o dispositivo correto no iOS7 e 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;
}

// Você também pode adicionar mais dispositivos (ieiPad).

Mohammad Zaid Pathan
fonte
A pergunta não é sobre o reconhecimento do dispositivo com base na resolução da tela. Além disso a Apple recomenda para criar UI adaptativa que é baseado em classes de tamanho não na detecção de dispositivo / tela
Julian Król
0

Usou a solução de mnemia levemente modificada, aquela sem verificação de versão do iOS, usando min / max nos limites da tela principal.
Eu precisava de um CGRect, então temos CGRectde limites MainScreen e mudou size.width=min(w,h), size.height=max(w,h). Em seguida, chamei a CGRectfunção get independente do sistema operacional em dois lugares no meu código, onde obtive o tamanho da tela OpenGL, toques etc. Antes de corrigir, eu tinha dois problemas - no IOS 8.x no modo paisagem, a posição de OpenGLexibição do modo paisagem estava incorreta: 1 / 4 de tela cheia na parte inferior esquerda. E os segundos toques retornaram valores inválidos. Ambos os problemas foram corrigidos conforme explicado. Obrigado!

Nenhum anjo
fonte