Jak uzyskać zależną od orientacji wysokość i szerokość ekranu?

96

Próbuję programowo określić aktualną wysokość i szerokość mojej aplikacji. Używam tego:

CGRect screenRect = [[UIScreen mainScreen] bounds];

Ale daje to szerokość 320 i wysokość 480, niezależnie od tego, czy urządzenie jest w orientacji pionowej czy poziomej. Jak mogę określić aktualną szerokość i wysokość (tj. W zależności od orientacji urządzenia) mojego głównego ekranu?

MusiGenesis
źródło

Odpowiedzi:

164

Możesz użyć czegoś podobnego UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)do określenia orientacji, a następnie odpowiednio użyć wymiarów.

JEDNAK, podczas zmiany orientacji jak w UIViewController's

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

Użyj orientacji przekazanej, toInterfaceOrientationponieważ statusBarOrientation UIApplication będzie nadal wskazywać na starą orientację, ponieważ jeszcze się nie zmieniła (ponieważ jesteś w module willobsługi zdarzeń).

Podsumowanie

Istnieje kilka powiązanych z tym postów, ale każdy z nich wydaje się wskazywać, że musisz:

  1. Spójrz, [[UIScreen mainScreen] bounds]aby uzyskać wymiary,
  2. Sprawdź, w jakiej jesteś orientacji i
  3. Uwzględnij wysokość paska stanu (jeśli jest pokazana)

Spinki do mankietów

Kodeks roboczy

Zwykle nie posuwam się tak daleko, ale wzbudziłeś moje zainteresowanie. Poniższy kod powinien załatwić sprawę. Napisałem kategorię na UIApplication. Dodałem metody klasowe, aby uzyskać currentSize lub rozmiar w danej orientacji, czyli to, co nazywasz w 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

Aby skorzystać z kodu wystarczy zadzwonić [UIApplication currentSize]. Uruchomiłem również powyższy kod, więc wiem, że działa i raportuje prawidłowe odpowiedzi we wszystkich orientacjach. Zwróć uwagę, że uwzględniam pasek stanu. Co ciekawe, musiałem odjąć MIN wysokości i szerokości paska stanu.

Mam nadzieję że to pomoże. :RE

Inne przemyślenia

Możesz przejść do uzyskania wymiarów, patrząc na właściwość UIWindow rootViewController. Patrzyłem na to w przeszłości i podobnie przedstawia te same wymiary zarówno w orientacji pionowej, jak i poziomej, z wyjątkiem tego, że informuje o przekształceniu rotacyjnym:

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

<UILayoutContainerView: 0xf7296f0; ramka = (0 0; 320 480); transformacja = [0, -1, 1, 0, 0, 0]; autorezacja = W + H; layer = <CALayer: 0xf729b80 >>

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

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

Nie jestem pewien, jak działa twoja aplikacja, ale jeśli nie używasz jakiegoś kontrolera nawigacyjnego, możesz mieć UIView pod głównym widokiem z maksymalną wysokością / szerokością rodzica i rosnąć / kurczyć się z rodzicem. Następnie można zrobić: [[[[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] subviews] objectAtIndex:0] frame]. W jednej linii wygląda to dość intensywnie, ale masz pomysł.

Jednak ... Nadal lepiej byłoby wykonać powyższe 3 kroki pod podsumowaniem. Zacznij zadzierać z UIWindows, a dowiesz się dziwnych rzeczy, na przykład pokazanie UIAlertView zmieni okno klucza UIApplication, aby wskazywało na nowe okno UIWindow utworzone przez UIAlertView. Kto wiedział? Zrobiłem to po znalezieniu błędu polegającego na keyWindow i odkryciu, że to się tak zmieniło!

Sam
źródło
5
Wydaje się, że jest to tak elementarne zadanie, że nie mogę uwierzyć, że muszę zhakować własny kod, aby ustalić coś takiego.
MusiGenesis
2
Spróbuję innego rozwiązania i opublikuję ponownie, jeśli zadziała. Ta strona jest tak agresywna, jeśli nie odpowiadasz szybko, równie dobrze możesz w ogóle nie odpowiadać. haha ...: Mam nadzieję, że moja odpowiedź jest przynajmniej pomocna. Znowu spróbuję czegoś innego bardzo szybko.
Sam
1
Wspaniały! (Swoją drogą, zainteresowanie wzbudziło) :-)
Vamos
1
Jeśli używasz applicationFramezamiast bounds(na UIScreen), nie musisz odejmować wysokości paska stanu.
aopsfan
2
stackoverflow.com/questions/24150359/... mainScreen().bounds.size stał się zależny od orientacji od iOS 8 i nowszych
Gerald
39

To jest mój kod rozwiązania! Ta metoda może dodać do klasy Categroy klasy NSObject lub możesz zdefiniować niestandardową klasę UIViewController Top i pozwolić wszystkim innym UIViewControllers na dziedziczenie.

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

Uwaga , po IOS8, jak mówi Apple Document of UIScreen's bounds property :

Dyskusja

Ten prostokąt jest określony w bieżącej przestrzeni współrzędnych, która uwzględnia wszelkie obroty interfejsu obowiązujące dla urządzenia. Dlatego wartość tej właściwości może się zmieniać, gdy urządzenie obraca się między orientacją pionową i poziomą.

więc ze względu na kompatybilność powinniśmy wykryć wersję IOS i wprowadzić zmiany jak poniżej:

#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
źródło
Prawie to samo, co moja odpowiedź, jedyne, czego mi brakuje, to to, że jest odwrócony w Portret. Ma to znaczenie tylko wtedy, gdy popierasz tę orientację.
Robert Wagstaff,
2
@Monjer nie powinieneś nazywać metod, które nie wykonują żądania GET, poprzedzonych i słowem get. currentScreenBoundsDependOnOrientation to lepsza nazwa metody
bogen
1
@ Hakonbogen.yes może masz rację, ponieważ deklaracja „peroperty” automatycznie generuje metodę ustawiającą / pobierającą, co może prowadzić do konfliktu nazw i sprzeczne z konwencjami nazewnictwa objc. Dziękuję za twoją radę.
monjer
Fajnie, że Apple w końcu milcząco przyznał, że ich kod rotacji był kompletnym bałaganem i właśnie zaczął nam mówić, jakie cholerne wymiary są w obecnej orientacji. Szkoda, że ​​dotarcie tam zajęło wersja 8.
MusiGenesis
30

Oto przydatne makro:

#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
źródło
14

W iOS 8+ należy skorzystać z viewWillTransitionToSize:withTransitionCoordinatormetody:

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

Zastępuje to przestarzałą metodę animacji, która nie podawała żadnych informacji o rozmiarze:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
Mark Hennings
źródło
To powinna być akceptowana odpowiedź. Nowoczesne i aktualne.
SarpErdag
Użyj tej odpowiedzi dla iOS 8 i nowszych.
iPhoneDeveloper
10

Od wersji iOS 8 granice ekranu są teraz zwracane prawidłowo dla bieżącej orientacji. Oznacza to, że iPad w orientacji poziomej [UIScreen mainScreen] .bounds zwróciłby 768 na iOS <= 7 i 1024 na iOS 8.

Poniższe zwraca prawidłową wysokość i szerokość we wszystkich wydanych wersjach.

-(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
źródło
8

jeśli chcesz mieć rozmiar zależny od orientacji i masz widok, możesz po prostu użyć:

view.bounds.size
Tod
źródło
ŚWIETNY! Rozwiązanie KISS = Keep It Simple and Stupid
bsorrentino
3
KISS nie oznacza „Keep it Simple and Stupid” - LOL! To znaczy „Mów prosto, głupio!” :-)
Erik van der Neut
Ta odpowiedź oczywiście działa tylko wtedy, gdy wiadomo, że twój widok jest dokładnie pełny ekran. Ale jeśli tak było, prawdopodobnie nie masz pierwotnego problemu opublikowanego przez OP.
Erik van der Neut
5

Pisałem kategorię UIScreen, która działa na wszystkich wersjach iOS, dzięki czemu można go używać tak:
[[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
źródło
1
To wygląda na najczystszą odpowiedź. Głosowano za.
Erik van der Neut
5

Oto szybki sposób na uzyskanie rozmiarów ekranu zależnych od orientacji:

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
}

Są to standardowe funkcje mojego projektu:

https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
źródło
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
źródło
0

Jednak w 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
źródło
0

użyj -> setNeedsDisplay () dla widoku, którego rozmiar chcesz zmienić.

Santosh
źródło