¿Cómo obtener la altura y el ancho de la pantalla en función de la orientación?

96

Estoy tratando de determinar mediante programación la altura y el ancho actuales de mi aplicación. Yo uso esto:

CGRect screenRect = [[UIScreen mainScreen] bounds];

Pero esto produce un ancho de 320 y una altura de 480, independientemente de si el dispositivo está en orientación vertical u horizontal. ¿Cómo puedo determinar el ancho y el alto actuales (es decir, dependiendo de la orientación del dispositivo) de mi pantalla principal?

MusiGenesis
fuente

Respuestas:

164

Puede usar algo como UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)para determinar la orientación y luego usar las dimensiones en consecuencia.

SIN EMBARGO, durante un cambio de orientación como en UIViewController

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

Utilice la orientación pasada, toInterfaceOrientationya que el statusBarOrientation de la UIApplication seguirá apuntando a la orientación anterior ya que aún no ha cambiado (ya que está dentro de un willcontrolador de eventos).

Resumen

Hay varias publicaciones relacionadas con esto, pero cada una de ellas parece indicar que tienes que:

  1. Mire [[UIScreen mainScreen] bounds]para obtener las dimensiones,
  2. Compruebe en qué orientación se encuentra y
  3. Tenga en cuenta la altura de la barra de estado (si se muestra)

Enlaces

Código de trabajo

Normalmente no voy tan lejos, pero despertaste mi interés. El siguiente código debería funcionar. Escribí una categoría sobre la aplicación UIA. Agregué métodos de clase para obtener el tamaño actual o el tamaño en una orientación determinada, que es lo que llamaría en 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

Para usar el código simple llame [UIApplication currentSize]. Además, ejecuté el código anterior, así que sé que funciona e informa las respuestas correctas en todas las orientaciones. Tenga en cuenta que tengo en cuenta la barra de estado. Curiosamente, tuve que restar el MIN de la altura y el ancho de la barra de estado.

Espero que esto ayude. :RE

Otros pensamientos

Puede obtener las dimensiones mirando la rootViewControllerpropiedad de UIWindow . He visto esto en el pasado y de manera similar informa las mismas dimensiones tanto en vertical como en horizontal, excepto que informa que tiene una transformación de rotación:

(gdb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] vista]

<UILayoutContainerView: 0xf7296f0; marco = (0 0; 320 480); transformar = [0, -1, 1, 0, 0, 0]; autoresize = W + H; layer = <CALayer: 0xf729b80 >>

(gdb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] vista]

<UILayoutContainerView: 0xf7296f0; marco = (0 0; 320 480); autoresize = W + H; layer = <CALayer: 0xf729b80 >>

No estoy seguro de cómo funciona su aplicación, pero si no está usando un controlador de navegación de algún tipo, podría tener una vista UIView bajo su vista principal con la altura / ancho máximo de padre y crecer / encoge con padre. Posteriormente, se podría hacer: [[[[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] subviews] objectAtIndex:0] frame]. Eso parece bastante intenso en una línea, pero entiendes la idea.

Sin embargo ... Sería mejor seguir los 3 pasos anteriores en el resumen. Comience a jugar con UIWindows y descubrirá cosas extrañas, como mostrar un UIAlertView cambiará la ventana clave de UIApplication para apuntar a una nueva UIWindow que creó UIAlertView. ¿Quien sabe? ¡Lo hice después de encontrar un error confiando en keyWindow y descubrir que cambió así!

Sam
fuente
5
Esto parece una tarea tan elemental que me cuesta creer que tengo que piratear mi propio código para determinar algo como esto.
MusiGenesis
2
Probaré otra solución y volveré a publicar si funciona. Este sitio es tan agresivo si no responde rápidamente, es posible que no responda en absoluto. jaja ...: Espero que mi respuesta sea al menos útil. De nuevo, intentaré algo más rápido.
Sam
1
¡Excelente! (Por cierto, uno tiene su interés despertado) :-)
Vamos
1
Si usa en applicationFramelugar de bounds(en la pantalla UIS), no tiene que restar la altura de la barra de estado.
aopsfan
2
stackoverflow.com/questions/24150359/… se mainScreen().bounds.size ha vuelto dependiente de la orientación desde iOS 8 en adelante
Gerald
39

¡Este es mi código de solución! Este método puede agregarse al Categroy de la clase NSObject, o puede definir una clase UIViewController personalizada superior, y dejar que todos sus otros UIViewControllers la hereden.

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

Tenga en cuenta , después de IOS8, como dice la propiedad de límites de Apple Document of UIScreen :

Discusión

Este rectángulo se especifica en el espacio de coordenadas actual, que tiene en cuenta las rotaciones de interfaz vigentes para el dispositivo. Por lo tanto, el valor de esta propiedad puede cambiar cuando el dispositivo gira entre las orientaciones vertical y horizontal.

Entonces, para considerar la compatibilidad, debemos detectar la versión de IOS y realizar el cambio de la siguiente manera:

#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
fuente
Casi igual que mi respuesta, lo único que falla es si está en Retrato al revés. Esto solo importa si apoya esa orientación.
Robert Wagstaff
2
@Monjer no debe nombrar métodos que no realicen una solicitud GET, con el prefijo i con la palabra get. currentScreenBoundsDependOnOrientation es un mejor nombre para el método
bogen
1
@ Hakonbogen.yes puede ser que tenga razón, porque la declaración "peroperty" genera automáticamente el método setter / getter, y esto puede llevar a un conflicto de nombres e ir en contra de las convenciones de nombres de objc. Gracias por su consejo.
monjer
Es bueno que Apple finalmente haya reconocido tácitamente que su código de rotación era un completo desastre, y acaba de comenzar a decirnos cuáles son las malditas dimensiones en la orientación actual. Lástima que tardó hasta la versión 8 para llegar allí.
MusiGenesis
30

Aquí hay una macro útil:

#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)
Robert Wagstaff
fuente
14

En iOS 8+ debes usar el viewWillTransitionToSize:withTransitionCoordinatormétodo:

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

Esto reemplaza el método de animación obsoleto que no proporcionó información sobre el tamaño:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
Mark Hennings
fuente
Esta debería ser la respuesta aceptada. Moderno y actualizado.
SarpErdag
Utilice esta respuesta para ios 8 y superior.
iPhoneDeveloper
10

A partir de iOS 8, los límites de pantalla ahora se devuelven correctos para la orientación actual. Esto significa que un iPad en orientación horizontal [UIScreen mainScreen] .bounds devolvería 768 en iOS <= 7 y 1024 en iOS 8.

Lo siguiente devuelve la altura y el ancho correctos en todas las versiones publicadas.

-(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 ;
}
A. Badger
fuente
8

si desea el tamaño dependiente de la orientación y tiene una vista, puede usar:

view.bounds.size
Tod
fuente
¡EXCELENTE! Solución KISS = Mantenlo simple y estúpido
bsorrentino
3
KISS no significa "Mantenlo simple y estúpido" - ¡LOL! Significa "¡Mantenlo simple, estúpido!" :-)
Erik van der Neut
Además, esta respuesta obviamente solo funciona si se sabe que su vista es exactamente a pantalla completa. Pero, si ese fuera el caso, entonces probablemente no tenga el problema original publicado por el OP.
Erik van der Neut
5

Escribí categoría para UIScreen, que funciona en todas las versiones de iOS, por lo que se pueden utilizar de esta manera:
[[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
ArtFeel
fuente
1
Esta me parece la respuesta más limpia. Votado a favor.
Erik van der Neut
5

Aquí hay una forma rápida de obtener tamaños de pantalla dependientes de la orientación:

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
}

Estos se incluyen como una función estándar en un proyecto mío:

https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
fuente
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
fuente
0

Sin embargo, en 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;
}
Gank
fuente
0

use -> setNeedsDisplay () para la vista que desea cambiar de tamaño.

Santosh
fuente