Apakah [UIScreen mainScreen] .bounds.size menjadi tergantung pada orientasi di iOS8?

184

Saya menjalankan kode berikut di iOS 7 dan 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);

Berikut ini adalah hasil dari iOS 8:

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

Dibandingkan dengan hasil di iOS 7:

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

Apakah ada dokumentasi yang menjelaskan perubahan ini? Atau apakah itu bug sementara di iOS 8 API?

lwxted
sumber
13
Yah keluaran iOS8 tampaknya jauh lebih logis
Levi

Jawaban:

173

Ya, itu tergantung pada orientasi di iOS8, bukan bug. Anda bisa meninjau sesi 214 dari WWDC 2014 untuk info lebih lanjut: "Lihat Kemajuan Kontroler di iOS 8"

Kutipan dari presentasi:

UIScreen sekarang berorientasi antarmuka:

  • [Batas layar UIS] sekarang berorientasi antarmuka
  • [UIScreen applicationFrame] sekarang berorientasi antarmuka
  • Pemberitahuan bingkai bilah status berorientasi pada antarmuka
  • Notifikasi bingkai keyboard berorientasi pada antarmuka
vhristoskov
sumber
7
Dalam ceramah WWDC yang disebutkan di atas, bagian yang berorientasi antarmuka dimulai pada 51:50.
cnotethegr8
2
Saya telah menghabiskan sebagian besar hari ini mencoba mengatasi kemajuan ini. Tampaknya itu tidak selalu terjadi, jadi jika Anda seorang pengembang SDK yang perlu bekerja di aplikasi lain, hal ini menjadi semakin rumit.
Glenn Maynard
1
@aroth Anda tidak dapat berinovasi tanpa mengerem beberapa kebiasaan lama.
Boris Y.
23
@borisy - Dengan hormat saya tidak setuju. Dalam kebanyakan kasus, mungkin berinovasi sambil menjaga kompatibilitas ke belakang. Mematahkan perubahan menampar kemalasan lebih dari apapun, menurut saya. Mereka ingin semua orang melakukan pekerjaan.
Agustus
1
Jawaban ini akan lebih membantu jika dijelaskan apa arti sebenarnya dari antarmuka berorientasi . Saya mungkin salah, tetapi saya pikir ketergantungan orientasi hanya berkaitan dengan pengontrol tampilan. Jika Anda mengambil kelas lain dan menghitung batas, mereka masih sesuai dengan gaya lama, selalu potret.
Maxi Mus
59

Ya, itu tergantung pada orientasi di iOS8.

Saya menulis metode Util untuk mengatasi masalah ini untuk aplikasi yang perlu mendukung versi OS yang lebih lama.

+ (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
sumber
1
Saya mengedit kode, mohon periksa, kondisi Anda untuk memeriksa versi OS salah
Jageen
@Jageen Tolong jelaskan, pemeriksaan versi berfungsi untuk saya. Apa masalahnya?
cbartel
Apa yang saya pahami adalah, kita perlu mengubah tinggi dan lebar jika OS adalah iOS8 dan orientasi adalah Landscape ,, apakah saya benar?
Jageen
@Jageen Negative, di iOS 8 metode ini bergantung pada orientasi. Logika Anda diaktifkan. Hasil edit Anda ditolak dengan benar.
cbartel
@cbartel apakah ada cara untuk mengkategorikan ini ke dalam UIScreen sehingga panggilan awal kami ke [UIScreen mainScreen] .bounds.size akan berfungsi untuk iOS 7 dan iOS 8?
kevinl
34

Ya, memang, ukuran layar sekarang orientasi tergantung pada iOS 8. Kadang-kadang, bagaimanapun, itu diinginkan untuk mendapatkan ukuran yang diperbaiki untuk orientasi potret. Inilah cara saya melakukannya.

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

    if ([screen respondsToSelector:@selector(fixedCoordinateSpace)]) {
                    return [screen.coordinateSpace convertRect:screen.bounds toCoordinateSpace:screen.fixedCoordinateSpace];
    } 
    return screen.bounds;
}
Maks
sumber
2
Saya menemukan bahwa ini memperbaiki masalah dengan aplikasi iPhone biasa, tetapi tidak dengan aplikasi iPhone yang berjalan di iPad.
ThomasW
32

Ya, sekarang tergantung pada orientasi.

Saya lebih suka metode di bawah ini untuk mendapatkan ukuran layar dalam orientasi-independen dengan beberapa jawaban di atas, baik karena lebih sederhana dan karena itu tidak bergantung pada salah satu kode orientasi (keadaan yang dapat bergantung pada waktu mereka dipanggil) atau memeriksa versi. Anda mungkin menginginkan perilaku iOS 8 yang baru, tetapi ini akan berhasil jika Anda perlu membuatnya stabil di semua versi iOS.

+(CGSize)screenSizeOrientationIndependent {
     CGSize screenSize = [UIScreen mainScreen].bounds.size;
     return CGSizeMake(MIN(screenSize.width, screenSize.height), MAX(screenSize.width, screenSize.height));
}
mnemia
sumber
Mungkin tidak akan bekerja pada sesuatu seperti Apple TV, di mana (hampir) selalu lebih lebar daripada tingginya.
cbh2000
11

Terkait dengan pertanyaan ini saat memecahkan masalah saya, berikut ini dua definisi yang saya gunakan untuk perhitungan lebar dan tinggi layar:

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

Jika Anda mendukung iOS 7 dan iOS 8, ini adalah solusi terbaik untuk masalah ini.

Antoine
sumber
@Namit Gupta, hasil edit Anda salah. Jika versi iOS 8 atau lebih, kita dapat menggunakan dimensi UIScreen karena ini tergantung pada orientasi. Sebelum iOS 8 Anda harus memeriksa orientasi bilah status. Hasil edit Anda sekarang mengatakan bahwa semua versi iOS yang tidak lebih rendah dari 8 seharusnya memeriksa orientasi status, yang salah.
Antoine
9

Anda dapat menggunakan nativeBounds(orientasi-independen)

nativeBounds

Persegi panjang pembatas layar fisik, diukur dalam piksel. (hanya baca)

Deklarasi SWIFT

  var nativeBounds: CGRect { get }

Kotak ini didasarkan pada perangkat dalam orientasi potret-up. Nilai ini tidak berubah ketika perangkat berputar.

Mendeteksi ketinggian perangkat:

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

}

Mendeteksi lebar perangkat:

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

}
Leo Dabus
sumber
3
Ketahuilah bahwa metode ini menghasilkan piksel, sementara sebagian besar metode lain menangani poin. Hal yang sangat berbeda.
jcsahnwaldt Reinstate Monica
1
Dalam kasus saya persis apa yang saya butuhkan :) (OpenGL atau Tampilan lain tidak didasarkan pada UIViews tetapi di mana saya perlu tahu dimensi piksel yang tepat)
Bersaelor
3
Peringatan: pada iphone 6+ ini mengembalikan 1080 x 1920 yang mungkin bukan yang Anda inginkan
Chanon
8

Ini bukan bug di iOS 8 SDK. Mereka membuat batas orientasi antarmuka tergantung. Menurut pertanyaan Anda tentang beberapa referensi atau dokumentasi untuk fakta itu saya akan sangat menyarankan Anda untuk menontonnya View Controller Advancements in iOS 8214 sesi dari WWDC 2014 . Bagian yang paling menarik (sesuai dengan keraguan Anda) adalah Screen Coordinatesmulai dari 50:45.

Julian Król
sumber
5

Ya, itu tergantung pada orientasi di iOS8.

Inilah cara Anda dapat memiliki cara yang konsisten untuk membaca batasan dalam mode iOS 8 di seluruh versi SDK dan 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
hfossli
sumber
1
Memeriksa versi SDK juga merupakan tip yang baik jika rekan kerja membangun aplikasi Anda dengan versi SDK yang berbeda (meskipun itu seharusnya tidak terjadi!).
Craig McMahon
4

Solusi saya adalah kombinasi dari MaxK dan hfossli. Saya membuat metode ini pada Kategori UIScreen dan tidak memiliki pemeriksaan versi (yang merupakan praktik buruk):

//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
sumber
3

Aku membutuhkan fungsi pembantu cepat yang terus perilaku yang sama seperti iOS7 bawah iOS8 - ini memungkinkan saya untuk menukar keluar saya [[UIScreen mainScreen] bounds]panggilan dan tidak menyentuh kode lain ...

+ (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
sumber
Jahat, tetapi menyelesaikan pekerjaan sampai waktu dapat dimasukkan ke dalam memperbaiki hal-hal dengan benar :-)
DuneCat
3

Metode di bawah ini dapat digunakan untuk menemukan batas layar untuk orientasi yang diberikan, terlepas dari versi iOS. Metode ini akan mengembalikan batasan berdasarkan ukuran layar perangkat dan akan memberikan nilai CGRect yang sama tanpa versi 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]; 

sumber
1

Itulah yang saya gunakan untuk menghitung rect yang benar:

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

Hanya menambahkan versi cepat fungsi cbartel yang sangat baik yang dijawab di atas.

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

Masalah saya terkait dengan bingkai UIWindows yang sedang minus. Jadi dibuat kode seperti di bawah ini di MyViewController - (NSUInteger) yang didukung Metode InterfaceOrientations

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

Dan ini pekerjaan untuk saya coba.

Hardik Mamtora
sumber
1

iOS 8 atau lebih tinggi

Solusi bagi mereka yang ingin mengetahui ukuran layar dalam poin (layar 3,5 inci memiliki 320 × 480 poin, layar 4,0 inci memiliki 320 × 568 poin, dll.)

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

Satu hal yang saya perhatikan adalah, urutan orientasi antarmuka yang didukung di Info.plist tidak masalah. Saya mendapat masalah dengan aplikasi ini (yang melakukan orientasi dalam kode), tetapi saya tidak menentukan di mana pun bahwa orientasi default adalah Portrait.

Saya pikir orientasi default adalah Portrait dalam hal apa pun.

Mengatur ulang itens di Info.plist, menempatkan Portrait terlebih dahulu, mengembalikan perilaku yang diharapkan.

epx
sumber
0

Ini akan memberikan perangkat yang benar di iOS7 dan iOS8 keduanya,

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

// Anda juga dapat menambahkan lebih banyak perangkat (ieiPad).

Mohammad Zaid Pathan
sumber
Pertanyaannya bukan tentang pengenalan perangkat berdasarkan resolusi layar. Selain itu Apple merekomendasikan untuk membuat UI adaptif yang didasarkan pada kelas ukuran bukan pada deteksi perangkat / layar
Julian Król
0

Menggunakan solusi mnemia yang sedikit dimodifikasi, yang tanpa versi iOS periksa, menggunakan min / max pada batas layar utama.
Aku butuh CGRectsehingga mendapat CGRectdari batas Mainscreen dan berubah size.width=min(w,h), size.height=max(w,h). Lalu saya sebut bahwa OS-independen mendapatkan CGRectfungsi di dua tempat dalam kode saya, di mana saya mendapatkan ukuran layar untuk OpenGL, menyentuh dll. Sebelum memperbaiki saya punya 2 masalah - pada iOS 8.x dalam mode tampilan posisi posisi tampilan landscape OpenGLsalah: 1 / 4 layar penuh di bagian kiri bawah. Dan sentuhan kedua mengembalikan nilai yang tidak valid. Kedua masalah diperbaiki seperti yang dijelaskan. Terima kasih!

Tidak ada malaikat
sumber