Czy [UIScreen mainScreen] .bounds.size staje się zależny od orientacji w iOS8?


184

Uruchomiłem następujący kod zarówno w systemie iOS 7, jak i 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);

Oto wynik z iOS 8:

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

W porównaniu do wyniku w iOS 7:

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

Czy istnieje dokumentacja określająca tę zmianę? Czy jest to tymczasowy błąd w interfejsach API systemu iOS 8?


13
Cóż, wyjście iOS8 wydaje się znacznie bardziej logiczne
Levi

Odpowiedzi:


173

Tak, to zależy od orientacji w iOS8, a nie błąd. Możesz przejrzeć sesję 214 z WWDC 2014, aby uzyskać więcej informacji: „Zobacz postępy kontrolera w iOS 8”

Cytat z prezentacji:

UIScreen jest teraz zorientowany na interfejs:

  • [Granice UIScreen] są teraz zorientowane na interfejs
  • [UIScreen applicationFrame] jest teraz zorientowany na interfejs
  • Powiadomienia ramek paska stanu są zorientowane na interfejs
  • Powiadomienia o ramce klawiatury są zorientowane na interfejs

7
We wspomnianym powyżej wykładzie WWDC część zorientowana na interfejs zaczyna się o 51:50.
cnotethegr8

2
Większość dzisiejszego dnia spędziłem na próbach obejścia tego postępu. Najwyraźniej nie zawsze tak się dzieje, więc jeśli jesteś programistą pakietu SDK, który musi działać w innych aplikacjach, te rzeczy stały się jeszcze bardziej skomplikowane.
Glenn Maynard,

1
@Ale nie możesz wprowadzać innowacji bez hamowania starych nawyków.
Boris Y.

23
@borisy - Z szacunkiem się nie zgadzam. W większości przypadków możliwe jest wprowadzanie innowacji przy jednoczesnym zachowaniu kompatybilności wstecznej. Moim zdaniem przełamywanie zmian jest bardziej leniwe niż cokolwiek innego. Chcą, aby wszyscy inni wykonali pracę.
aroth

1
Ta odpowiedź byłaby jeszcze bardziej pomocna, gdyby wyjaśniono, co tak naprawdę oznacza interfejs . Mogę się mylić, ale myślę, że zależność orientacji dotyczy tylko kontrolerów widoku. Jeśli weźmiesz inną klasę i obliczysz granice, są one nadal zgodne ze starym stylem, zawsze portret.
Maxi Mus

59

Tak, zależy od orientacji w iOS8.

Napisałem metodę Util, aby rozwiązać ten problem dla aplikacji, które muszą obsługiwać starsze wersje systemu operacyjnego.

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

1
Zredagowałem kod, proszę sprawdzić, warunek sprawdzania wersji systemu operacyjnego był nieprawidłowy
Jageen

@Jageen Wyjaśnij, sprawdzanie wersji działa dla mnie. Jaki jest problem?
cartart

Rozumiem, że musimy zmienić wysokość i szerokość, jeśli system operacyjny to iOS8, a orientacja to Krajobraz, prawda?
Jageen

@Jageen Negative, w iOS 8 metoda zależy od orientacji. Twoja logika jest przełączona. Twoja edycja została poprawnie odrzucona.
cartart

@cartart to sposób na zaklasyfikowanie tego do UIScreen, aby nasze oryginalne połączenie z [UIScreen mainScreen] .bounds.size działało zarówno na iOS 7, jak i iOS 8?
kevinl

34

Tak, rzeczywiście, rozmiar ekranu jest teraz zależny od orientacji w iOS 8. Czasami jednak pożądane jest, aby uzyskać rozmiar ustawiony na orientację pionową. Oto jak to robię.

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

    if ([screen respondsToSelector:@selector(fixedCoordinateSpace)]) {
                    return [screen.coordinateSpace convertRect:screen.bounds toCoordinateSpace:screen.fixedCoordinateSpace];
    } 
    return screen.bounds;
}

2
Uważam, że to rozwiązuje problem ze zwykłymi aplikacjami na iPhone'a, ale nie na aplikacjach na iPhone'a uruchomionych na iPadzie.
ThomasW,

32

Tak, teraz zależy to od orientacji.

Wolę poniższą metodę uzyskiwania rozmiaru ekranu w sposób niezależny od orientacji od niektórych powyższych odpowiedzi, zarówno dlatego, że jest prostszy, jak i ponieważ nie zależy od żadnego kodu orientacji (którego stan może zależeć od czas ich wywołania) lub podczas sprawdzania wersji. Możesz chcieć nowego zachowania iOS 8, ale zadziała, jeśli chcesz, aby był stabilny we wszystkich wersjach iOS.

+(CGSize)screenSizeOrientationIndependent {
     CGSize screenSize = [UIScreen mainScreen].bounds.size;
     return CGSizeMake(MIN(screenSize.width, screenSize.height), MAX(screenSize.width, screenSize.height));
}

Prawdopodobnie nie będzie działać na czymś takim jak Apple TV, gdzie jest (prawie) zawsze szerszy niż wysoki.
cbh2000,

11

Powiązane z tym pytaniem, ponieważ rozwiązało mój problem, tutaj dwie definicje używam do obliczeń szerokości i wysokości ekranu:

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

Jeśli obsługujesz zarówno iOS 7, jak i iOS 8, jest to najlepsze rozwiązanie tego problemu.


@Namit Gupta, twoja edycja jest nieprawidłowa. Jeśli wersja IOS ma 8 lub więcej, możemy użyć wymiarów UIScreen, ponieważ są one zależne od orientacji. Przed iOS 8 trzeba było sprawdzić orientację paska stanu. Twoja edycja mówi teraz, że wszystkie wersje iOS nie niższe niż 8 powinny sprawdzać orientację paska stanu, co jest nieprawidłowe.
Antoine

9

Możesz użyć nativeBounds(niezależnie od orientacji)

nativeBounds

Prostokąt ograniczający ekranu fizycznego, mierzony w pikselach. (tylko czytać)

Deklaracja SWIFT

  var nativeBounds: CGRect { get }

Ten prostokąt opiera się na urządzeniu w orientacji pionowej. Ta wartość nie zmienia się, gdy urządzenie się obraca.

Wykrywanie wysokości urządzenia:

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

}

Wykrywanie szerokości urządzenia:

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

}

3
Należy pamiętać, że ta metoda zwraca piksele, podczas gdy większość innych metod zajmuje się punktami. Bardzo różne rzeczy.
jcsahnwaldt Reinstate Monica

1
W moim przypadku dokładnie to, czego potrzebowałem :) (OpenGL lub inne widoki nie oparte na UIViews, ale gdzie muszę znać dokładne wymiary w pikselach)
Bersaelor

3
Ostrzeżenie: na iPhonie 6+ zwrócił 1080 x 1920, co może nie być tym, czego chcesz
Chanon

8

To nie jest błąd w zestawie SDK iOS 8. Uzależnili orientację interfejsu granic. Zgodnie z twoim pytaniem o odniesienie lub dokumentację tego faktu gorąco polecam obejrzenie View Controller Advancements in iOS 8214 sesji z WWDC 2014 . Najciekawsza część (według twoich wątpliwości) Screen Coordinateszaczyna się o 50:45.


5

Tak, zależy od orientacji w iOS8.

Oto, w jaki sposób możesz uzyskać spójny sposób odczytywania granic w stylu iOS 8 w wersjach SDK i OS.

#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

1
Sprawdzanie wersji SDK jest również dobrą wskazówką, jeśli koledzy budują twoją aplikację z różnymi wersjami SDK (choć nie powinno tak się dziać!).
Craig McMahon

4

Moje rozwiązanie to połączenie MaxK i hfossli. Zrobiłem tę metodę na kategorii UIScreen i nie ma ona kontroli wersji (co jest złą praktyką):

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

3

Potrzebowałem funkcji szybkiego pomocnika, która zachowałaby to samo zachowanie, co iOS7 pod iOS8 - to pozwoliło mi zamienić moje [[UIScreen mainScreen] bounds]połączenia i nie dotykać innego kodu ...

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

Paskudne, ale wykonuje pracę, dopóki nie można poświęcić czasu na prawidłowe naprawienie rzeczy :-)
DuneCat

3

Poniższej metody można użyć do znalezienia granic ekranu dla danej orientacji, niezależnie od wersji iOS. Ta metoda zwróci granice na podstawie rozmiaru ekranu urządzenia i da tę samą wartość CGRect niezależnie od wersji 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]; 

1

Właśnie tego użyłem do obliczenia poprawnego odbicia:

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

1

Właśnie dodałem szybką wersję doskonałej funkcji cbartel, na którą odpowiedziano powyżej.

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
}

1

Mój problem związany był z ramką UIWindows, która działała w minusie. Więc utworzyłem kod jak poniżej w MyViewController - (NSUInteger) obsługiwaneInterfaceOrientations Metoda

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

I jego praca dla mnie spróbuj.


1

iOS 8 lub nowszy

Rozwiązaniem dla tych, którzy chcą dowiedzieć się rozmiar ekranu w punktach (ekran 3,5 cala ma 320 x 480 punktów, ekran 4,0 cala ma 320 x 568 punktów itp.),

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

0

Zwróciłem uwagę na to, że kolejność obsługiwanych orientacji interfejsu w Info.plist ma znaczenie. Mam problem z tym pytaniem w mojej aplikacji (która orientuje się w kodzie), ale nigdzie nie określiłem, że domyślna orientacja to Portret.

W każdym razie myślałem, że domyślną orientacją jest Portret.

Przestawienie itens w Info.plist, stawiając Portret na pierwszym miejscu, przywróciło oczekiwane zachowanie.


0

To da prawidłowe urządzenie w iOS7 i iOS8 zarówno,

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

// Możesz również dodać więcej urządzeń (ieiPad).


Pytanie nie dotyczy rozpoznawania urządzenia na podstawie rozdzielczości ekranu. Ponadto Apple zaleca tworzenie adaptacyjnego interfejsu użytkownika opartego na klasach wielkości, a nie na wykrywaniu urządzenia / ekranu
Julian Król,

0

Użyto nieco zmodyfikowanego rozwiązania mnemii, tego bez sprawdzania wersji iOS, używając min / max na granicach ekranu głównego.
I potrzebna jest CGRectwięc dostał CGRectod mainScreen granice i zmienił size.width=min(w,h), size.height=max(w,h). Następnie nazwałem tę niezależną od systemu operacyjnego CGRectfunkcję get w dwóch miejscach w kodzie, gdzie otrzymuję rozmiar ekranu OpenGL, dotknięcia itp. Przed naprawą miałem 2 problemy - na IOS 8.x w trybie poziomym pozycja OpenGLwyświetlania była nieprawidłowa: 1 / 4 pełnego ekranu w lewej dolnej części. Drugie dotknięcia zwróciły nieprawidłowe wartości. Oba problemy zostały naprawione, jak wyjaśniono. Dzięki!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.