Come ottenere l'altezza e la larghezza dello schermo dipendenti dall'orientamento?


96

Sto cercando di determinare a livello di codice l'altezza e la larghezza correnti della mia applicazione. Io uso questo:

CGRect screenRect = [[UIScreen mainScreen] bounds];

Ma questo produce una larghezza di 320 e un'altezza di 480, indipendentemente dal fatto che il dispositivo sia in orientamento verticale o orizzontale. Come posso determinare la larghezza e l'altezza correnti (cioè in base all'orientamento del dispositivo) della mia schermata principale?

Risposte:


164

Puoi usare qualcosa di simile UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)per determinare l'orientamento e quindi utilizzare le dimensioni di conseguenza.

TUTTAVIA, durante un cambio di orientamento come in UIViewController

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

Usa l'orientamento passato toInterfaceOrientationpoiché lo statusBarOrientation di UIApplication punterà ancora al vecchio orientamento poiché non è ancora cambiato (poiché sei all'interno di un willgestore di eventi).

Sommario

Ci sono diversi post correlati a questo, ma ognuno di essi sembra indicare che devi:

  1. Guarda [[UIScreen mainScreen] bounds]per ottenere le dimensioni,
  2. Controlla l'orientamento in cui ti trovi e
  3. Considera l'altezza della barra di stato (se visualizzata)

Collegamenti

Codice di lavoro

Di solito non vado così lontano, ma hai suscitato il mio interesse. Il codice seguente dovrebbe fare il trucco. Ho scritto una categoria su UIApplication. Ho aggiunto metodi di classe per ottenere currentSize o la dimensione in un determinato orientamento, che è ciò che chiameresti in 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

Per utilizzare il codice semplice chiamata [UIApplication currentSize]. Inoltre, ho eseguito il codice sopra, quindi so che funziona e riporta le risposte corrette in tutti gli orientamenti. Notare che fattore nella barra di stato. È interessante notare che ho dovuto sottrarre il MIN dell'altezza e della larghezza della barra di stato.

Spero che questo ti aiuti. : D

Altri pensieri

Potresti ottenere le dimensioni guardando la rootViewControllerproprietà di UIWindow . L'ho esaminato in passato e riporta allo stesso modo le stesse dimensioni sia in verticale che in orizzontale, tranne per il fatto che riporta una trasformazione di rotazione:

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

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

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

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

Non sei sicuro di come funzioni la tua app, ma se non stai utilizzando un controller di navigazione di qualche tipo, potresti avere una vista UIV sotto la tua vista principale con l'altezza / larghezza massima del genitore e cresce / si restringe con il genitore. Poi si potrebbe fare: [[[[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] subviews] objectAtIndex:0] frame]. Sembra piuttosto intenso su una riga, ma hai un'idea.

Tuttavia ... Sarebbe comunque meglio eseguire i 3 passaggi precedenti sotto il riepilogo. Inizia a fare scherzi con UIWindows e scoprirai cose strane, come mostrare un UIAlertView cambierà la finestra dei tasti di UIApplication in modo che punti a una nuova UIWindow creata da UIAlertView. Chi lo sapeva? L'ho fatto dopo aver trovato un bug affidandomi a keyWindow e dopo aver scoperto che era cambiato così!


5
Sembra un compito così elementare che ho difficoltà a credere di dover hackerare il mio codice per determinare qualcosa di simile.
MusiGenesis

2
Proverò un'altra soluzione e ripubblicherò se funziona. Questo sito è così aggressivo se non rispondi rapidamente che potresti anche non rispondere affatto. haha ...: Spero che la mia risposta sia almeno utile. Ancora una volta, proverò qualcos'altro molto velocemente.
Sam

1
Grande! (A proposito, uno ha stuzzicato il proprio interesse) :-)
Vamos

1
Se usi applicationFrameinvece di bounds(su UIScreen), non devi sottrarre l'altezza della barra di stato.
aopsfan

2
stackoverflow.com/questions/24150359/… mainScreen().bounds.size è diventato dipendente dall'orientamento da iOS 8 in poi
Gerald

39

Questo è il codice della mia soluzione! Questo metodo può essere aggiunto al Categroy della classe NSObject, oppure puoi definire una classe UIViewController personalizzata Top e lasciare che tutti gli altri UIViewControllers la ereditino.

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

Nota , dopo IOS8, come dice la proprietà bounds del documento Apple di UIScreen :

Discussione

Questo rettangolo è specificato nello spazio delle coordinate corrente, che tiene conto di eventuali rotazioni dell'interfaccia in vigore per il dispositivo. Pertanto, il valore di questa proprietà può cambiare quando il dispositivo ruota tra l'orientamento verticale e quello orizzontale.

quindi per la considerazione della compatibilità, dovremmo rilevare la versione IOS e apportare la modifica come di seguito:

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

Più o meno come la mia risposta, l'unica cosa che manca è se è in Ritratto capovolto. Questo è importante solo se sostieni quell'orientamento.
Robert Wagstaff

2
@ Monjer non dovresti nominare metodi che non eseguono una richiesta GET, anteponendo i con la parola get. currentScreenBoundsDependOnOrientation è un nome migliore per il metodo
bogen

1
@ Hakonbogen.yes potresti avere ragione, perché la dichiarazione "peroperty" genera automaticamente il metodo setter / getter, e questo può portare a conflitti di denominazione e andare contro le convenzioni di denominazione di objc. Grazie per il tuo consiglio.
monjer

È bello che Apple abbia finalmente tacitamente riconosciuto che il loro codice di rotazione era un completo disastro, e ha appena iniziato a dirci quali sono le dannate dimensioni nell'orientamento attuale. Peccato che ci sia voluta fino alla versione 8 per arrivarci.
MusiGenesis

30

Ecco una pratica macro:

#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

In iOS 8+ dovresti usare il viewWillTransitionToSize:withTransitionCoordinatormetodo:

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

Questo sostituisce il metodo di animazione deprecato che non forniva informazioni sulla dimensione:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration

Questa dovrebbe essere la risposta accettata. Moderno e aggiornato.
SarpErdag

Usa questa risposta per iOS 8 e versioni successive.
iPhoneDeveloper

10

A partire da iOS 8, i limiti dello schermo vengono ora restituiti corretti per l'orientamento corrente. Ciò significa che un iPad con orientamento orizzontale [UIScreen mainScreen] .bounds restituirà 768 su iOS <= 7 e 1024 su iOS 8.

Quanto segue restituisce l'altezza e la larghezza corrette su tutte le versioni rilasciate.

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

se vuoi la dimensione dipendente dall'orientamento e hai una vista, puoi semplicemente usare:

view.bounds.size

GRANDE! Soluzione KISS = Keep It Simple and Stupid
bsorrentino

3
KISS non significa "Keep it Simple and Stupid" - LOL! Significa "Sii semplice, stupido!" :-)
Erik van der Neut

Inoltre, questa risposta ovviamente funziona solo se si sa che la visualizzazione è esattamente a schermo intero. Ma, se fosse così, probabilmente non hai il problema originale pubblicato dall'OP.
Erik van der Neut

5

Ho scritto categoria per UIScreen, che funziona su tutte le versioni di iOS, in modo da poter usare in questo modo:
[[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

1
Questa mi sembra la risposta più pulita. Up-votato.
Erik van der Neut

5

Ecco un modo rapido per ottenere dimensioni dello schermo dipendenti dall'orientamento:

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
}

Questi sono inclusi come funzione standard in un mio progetto:

https://github.com/goktugyil/EZSwiftExtensions


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

0

Tuttavia, su 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

usa -> setNeedsDisplay () per la vista che vuoi ridimensionare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.