Phát hiện nếu thiết bị là iPhone X


262

Ứng dụng iOS của tôi sử dụng chiều cao tùy chỉnh UINavigationBardẫn đến một số vấn đề trên iPhone X mới.

Có ai đó đã biết cách phát hiện đáng tin cậy theo chương trình (trong Objective-C) nếu một ứng dụng đang chạy trên iPhone X chưa?

BIÊN TẬP:

Tất nhiên, việc kiểm tra kích thước của màn hình là có thể, tuy nhiên, tôi tự hỏi liệu có một phương pháp "tích hợp" nào như TARGET_OS_IPHONEphát hiện iOS ...

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
    if (screenSize.height == 812)
        NSLog(@"iPhone X");
}

EDIT 2:

Tôi không nghĩ rằng câu hỏi của tôi là một bản sao của câu hỏi được liên kết. Tất nhiên, có các phương pháp để "đo lường" các thuộc tính khác nhau của thiết bị hiện tại và sử dụng kết quả để quyết định thiết bị nào được sử dụng. Tuy nhiên, đây không phải là điểm thực sự của câu hỏi của tôi khi tôi cố gắng nhấn mạnh trong lần chỉnh sửa đầu tiên.

Câu hỏi thực tế là: "Có thể phát hiện trực tiếp nếu thiết bị hiện tại là iPhone X (ví dụ: bằng một số tính năng SDK) hay tôi phải sử dụng các phép đo gián tiếp" ?

Bằng các câu trả lời được đưa ra cho đến nay, tôi cho rằng câu trả lời là "Không, không có phương pháp trực tiếp. Các phép đo là con đường để đi".


iPhone X có độ phân giải màn hình khác với những người khác.
El Tomato

2
Có, như tôi đã đề cập trong phần chỉnh sửa của mình, có thể kiểm tra kích thước màn hình. Tuy nhiên, câu hỏi là, nếu có phương pháp "trực tiếp" để truy vấn loại thiết bị thay vì đo "gián tiếp" ...
Andrei Herford

3
Tác giả chỉ muốn lấy loại thiết bị chứ không phải độ phân giải màn hình. Tại sao không kiểm tra tên máy trực tiếp? @lubilis nói đúng.
Itachi

2
Tại sao bạn không sử dụng các hướng dẫn về khu vực an toàn theo khuyến nghị của Apple?
holex

4
QUAN TRỌNG, các nhà phát triển trong tương lai: Không phát hiện chiều cao màn hình sử dụng này vì các giải pháp hàng đầu hiện tại cho thấy, điều này thật tệ vì nó có thể dẫn đến kết quả dương tính giả cho các thiết bị trong tương lai; sẽ không hoạt động nếu UIWindow chưa được hiển thị (như trong các hàm init AppDelegate của bạn), sẽ không hoạt động trong các ứng dụng ngang và có thể thất bại trên trình giả lập nếu đặt tỷ lệ. KHÔNG BAO GIỜ sử dụng số ma thuật cho những thứ như thế này! Bạn có thể kiểm tra cờ phần cứng để đảm bảo thành công như tôi đã làm ở đây: stackoverflow.com/a/51511947/2057171
Albert Renshaw

Câu trả lời:


383

Dựa trên câu hỏi của bạn, câu trả lời là không. Không có phương pháp trực tiếp. Để biết thêm thông tin bạn có thể lấy thông tin ở đây:

Chiều cao iPhone X là 2436 px

Từ kích thước màn hình thiết bị và độ phân giải :

nhập mô tả hình ảnh ở đây

Từ kích thước và hướng màn hình thiết bị :

nhập mô tả hình ảnh ở đây

Swift 3 trở lên :

if UIDevice().userInterfaceIdiom == .phone {
    switch UIScreen.main.nativeBounds.height {
        case 1136:
            print("iPhone 5 or 5S or 5C")

        case 1334:
            print("iPhone 6/6S/7/8")

        case 1920, 2208:
            print("iPhone 6+/6S+/7+/8+")

        case 2436:
            print("iPhone X/XS/11 Pro")

        case 2688:
            print("iPhone XS Max/11 Pro Max")

        case 1792:
            print("iPhone XR/ 11 ")

        default:
            print("Unknown")
        }
    }

Mục tiêu-C :

if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
    switch ((int)[[UIScreen mainScreen] nativeBounds].size.height) {
        case 1136:
            printf("iPhone 5 or 5S or 5C");
                break;

        case 1334:
            printf("iPhone 6/6S/7/8");
            break;

        case 1920, 2208:
            printf("iPhone 6+/6S+/7+/8+");
            break;

       case 2436:
            print("iPhone X/XS/11 Pro");
             break;

        case 2688:
            print("iPhone XS Max/11 Pro Max");
             break;

        case 1792:
            print("iPhone XR/ 11 ");
             break;

        default:
            printf("Unknown");
            break;
    }
}

Xamarin.iOS :

if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone) {
    if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1136) {
        Console.WriteLine("iPhone 5 or 5S or 5C");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1334) {
        Console.WriteLine("iPhone 6/6S/7/8");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1920 || (UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2208) {
        Console.WriteLine("iPhone 6+/6S+/7+/8+");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2436) {
        Console.WriteLine("iPhone X, XS, 11 Pro");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2688) {
        Console.WriteLine("iPhone XS Max, 11 Pro Max");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1792) {
        Console.WriteLine("iPhone XR, 11");
    } else {
        Console.WriteLine("Unknown");
    }
}

Dựa trên câu hỏi của bạn như sau:

Hoặc sử dụng screenSize.heightnhư float 812.0f không int 812.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
        // 812.0 on iPhone X, XS
        // 896.0 on iPhone XS Max, XR.

    if (screenSize.height >= 812.0f)
        NSLog(@"iPhone X");
    }

Để biết thêm thông tin, bạn có thể tham khảo trang sau trong Nguyên tắc giao diện con người của iOS:

Swift :

Phát hiện với topNotch:

Nếu bất cứ ai xem xét việc sử dụng notch để phát hiện iPhoneX, hãy nhớ rằng trên "phong cảnh" của nó giống nhau cho tất cả iPhone.

var hasTopNotch: Bool {
    if #available(iOS 13.0,  *) {
        return UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.safeAreaInsets.top ?? 0 > 20
    }else{
     return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
    }

    return false
}

Mục tiêu-C :

- (BOOL)hasTopNotch {
   if (@available(iOS 13.0, *)) {
       return [self keyWindow].safeAreaInsets.top > 20.0;
   }else{
       return [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top > 20.0;
   }
   return  NO;
}

- (UIWindow*)keyWindow {
    UIWindow        *foundWindow = nil;
    NSArray         *windows = [[UIApplication sharedApplication]windows];
    for (UIWindow   *window in windows) {
        if (window.isKeyWindow) {
            foundWindow = window;
            break;
        }
    }
    return foundWindow;
}

CẬP NHẬT :

Không sử dụng thuộc userInterfaceIdiomtính để xác định loại thiết bị, như tài liệu cho userInterfaceIdiom giải thích:

Đối với các ứng dụng phổ quát, bạn có thể sử dụng thuộc tính này để điều chỉnh hành vi của ứng dụng của mình cho một loại thiết bị cụ thể. Ví dụ: thiết bị iPhone và iPad có kích thước màn hình khác nhau, vì vậy bạn có thể muốn tạo các chế độ xem và điều khiển khác nhau dựa trên loại thiết bị hiện tại.

Đó là, thuộc tính này chỉ được sử dụng để xác định kiểu xem của ứng dụng đang chạy. Tuy nhiên, ứng dụng iPhone (không phải phổ quát) có thể được cài đặt trong thiết bị iPad thông qua cửa hàng Ứng dụng, trong trường hợp đó, userInterfaceIdiomcũng sẽ trả lại UIUserInterfaceIdiomPhone.

Cách đúng là lấy tên máy qua uname. Kiểm tra các chi tiết sau:


Độ phân giải của iPhone X là 2436 x 1125 pixel theo: iphonesoft.fr/2017/09/12/iêu
Medhi

1
@Medhi - độ phân giải của iphone X là - 1125 x 2436 pixel (mật độ pixel ~ 458 ppi)
Anbu.Karthik

14
KHÔNG! Ứng dụng iPhone (không phải vũ trụ) có thể được cài đặt trong thiết bị iPad thông qua App store, trong trường hợp đó,userInterfaceIdiomcũng sẽ trả lạiUIUserInterfaceIdiomPhone. Câu trả lời này là sai.
Itachi

1
@ThreeCoins, vui lòng cập nhật câu trả lời của bạn cho các thiết bị cộng theo đề xuất của Leo Dabus. Nó hoạt động trên trình giả lập Plus nhưng không phải trên thiết bị.
Hiren Gujarati

2
Điều này là xấu vì nó có thể dẫn đến kết quả dương tính giả cho các thiết bị trong tương lai; sẽ không hoạt động nếu UIWindow chưa được kết xuất (AppDelegate), sẽ không hoạt động trong các ứng dụng ngang và có thể thất bại trên trình giả lập nếu đặt tỷ lệ. Bạn có thể kiểm tra cờ phần cứng để đảm bảo thành công như tôi đã làm ở đây: stackoverflow.com/a/51511947/2057171
Albert Renshaw

101

Một khả năng khác, hoạt động trên iOS 11 và iOS 12 vì iPhone X là thiết bị duy nhất có một notch ở phía trên và một hình nhỏ của 44. Đó là những gì tôi thực sự phát hiện ở đây:

Mục tiêu-C:

    BOOL iPhoneX = NO;
    if (@available(iOS 11.0, *)) {
        UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
        if (mainWindow.safeAreaInsets.top > 24.0) {
            iPhoneX = YES;
        }
    }

Swift 4:

/// Has safe area
///
/// with notch: 44.0 on iPhone X, XS, XS Max, XR.
///
/// without notch: 20.0 on iPhone 8 on iOS 12+.
///
static var hasSafeArea: Bool {
    guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, topPadding > 24 else {
        return false
    }
    return true
}

Và tất nhiên, bạn có thể cần kiểm tra các miếng đệm an toàn bên trái và bên phải nếu bạn đang ở hướng ngang.

Chỉnh sửa: _window là UIWindow của AppDelegate, nơi kiểm tra này được thực hiện trong ứng dụng didFinishLaunchingWithOptions.

Trả lời được cập nhật cho iOS 12 để kiểm tra xem top> 24 chứ không phải top> 0.

Chỉnh sửa: Trong trình giả lập, bạn có thể truy cập Phần cứng, Chuyển đổi Thanh trạng thái trong cuộc gọi. Làm điều đó cho tôi thấy rằng chiều cao của thanh trạng thái không thay đổi trên iPhone X trên iOS 11 hoặc iPhone XS iOS 12 khi tham gia vào một cuộc gọi. Tất cả những thay đổi đó là biểu tượng thời gian, có nền màu xanh lục, trong cả hai trường hợp. Đây là một snap:

nhập mô tả hình ảnh ở đây


5
Các phần trong vùng an toàn sẽ chứa chiều cao của thanh trạng thái, nếu có thể nhìn thấy, trên các thiết bị khác. Kiểm tra nếu đây là 0 sẽ chỉ cho bạn biết nếu thanh trạng thái hiển thị, chứ không phải nếu thiết bị là iPhone X.
IMcD23

3
"Điều này có thể phá vỡ trong iPhone X hoặc iPhone 11 ", Cook nói.
Itachi

11
Tôi đã điều chỉnh một chút và sử dụng if _window.safeAreaInsets != UIEdgeInsets.zerođể cho phép mọi hướng thiết bị
Fraser

2
Nếu bạn không muốn sử dụng .top, safeAreaInsets.bottomsẽ là 34 trên iPhone X và 0 trên các thiết bị khác
blwinters 7/07/18

7
Cảnh báo: Không sử dụng cái này, nó bị hỏng trên iOS 12. Nó cũng không được ghi lại những gì UIWindow nên làm trong trường hợp này. openradar.appspot.com/42372793
steipete

73

Bạn sẽ thực hiện các phát hiện khác nhau của iPhone X tùy thuộc vào nhu cầu thực tế.

để giao dịch với đỉnh cao (thanh trạng thái, thanh điều hướng), v.v.

class var hasTopNotch: Bool {
    if #available(iOS 11.0, tvOS 11.0, *) {
        // with notch: 44.0 on iPhone X, XS, XS Max, XR.
        // without notch: 24.0 on iPad Pro 12.9" 3rd generation, 20.0 on iPhone 8 on iOS 12+.
        return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 24
    }
    return false
}

để giao dịch với chỉ báo nhà dưới cùng (thanh tab), v.v.

class var hasBottomSafeAreaInsets: Bool {
    if #available(iOS 11.0, tvOS 11.0, *) {
        // with home indicator: 34.0 on iPhone X, XS, XS Max, XR.
        // with home indicator: 20.0 on iPad Pro 12.9" 3rd generation.
        return UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0 > 0
    }
    return false
}

cho kích thước hình nền, tính năng toàn màn hình, vv

class var isIphoneXOrBigger: Bool {
    // 812.0 on iPhone X, XS.
    // 896.0 on iPhone XS Max, XR.
    return UIScreen.main.bounds.height >= 812
}

Lưu ý: cuối cùng trộn nó với UIDevice.current.userInterfaceIdiom == .phone
Lưu ý: phương pháp này yêu cầu phải có bảng phân cảnh LaunchScreen hoặc LaunchImages thích hợp

cho tỷ lệ nền, tính năng cuộn, vv

class var isIphoneXOrLonger: Bool {
    // 812.0 / 375.0 on iPhone X, XS.
    // 896.0 / 414.0 on iPhone XS Max, XR.
    return UIScreen.main.bounds.height / UIScreen.main.bounds.width >= 896.0 / 414.0
}

Lưu ý: phương pháp này yêu cầu phải có bảng phân cảnh LaunchScreen hoặc LaunchImages thích hợp

để phân tích, thống kê, theo dõi, v.v.

Lấy mã định danh máy và so sánh nó với các giá trị được ghi lại:

class var isIphoneX: Bool {
    var size = 0
    sysctlbyname("hw.machine", nil, &size, nil, 0)
    var machine = [CChar](repeating: 0, count: size)
    sysctlbyname("hw.machine", &machine, &size, nil, 0)
    let model = String(cString: machine)
    return model == "iPhone10,3" || model == "iPhone10,6"
}

Để bao gồm trình giả lập dưới dạng iPhone X hợp lệ trong phân tích của bạn:

class var isIphoneX: Bool {
    let model: String
    if TARGET_OS_SIMULATOR != 0 {
        model = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] ?? ""
    } else {
        var size = 0
        sysctlbyname("hw.machine", nil, &size, nil, 0)
        var machine = [CChar](repeating: 0, count: size)
        sysctlbyname("hw.machine", &machine, &size, nil, 0)
        model = String(cString: machine)
    }
    return model == "iPhone10,3" || model == "iPhone10,6"
}

Để bao gồm iPhone XS, XS Max và XR, chỉ cần tìm các mẫu bắt đầu bằng "iPhone11":

return model == "iPhone10,3" || model == "iPhone10,6" || model.starts(with: "iPhone11,")

để được hỗ trợ faceID

import LocalAuthentication
/// will fail if user denies canEvaluatePolicy(_:error:)
class var canUseFaceID: Bool {
    if #available(iOS 11.0, *) {
        return LAContext().biometryType == .typeFaceID
    }
    return false
}

Tôi đã hy vọng rằng nó return LAContext().biometryType == .typeFaceIDsẽ hoạt động ngay cả khi người dùng đã từ chối canEvaliatedPolicy, nhưng nó không hoạt động với tôi, nó vẫn quay trở lại.none
Jeremy

Chà @Jeremy, đó là một hành vi được ghi chép lại, hậu quả của chính sách quyền riêng tư của Apple. Đó là lý do tại sao các bình luận trên phương pháp.
Cœur

Ah, tôi hiểu sai ý kiến ​​của bạn. Tôi nghĩ rằng bạn có nghĩa là sử dụng canEvaliatedPolicy có thể thất bại, vì vậy sử dụng sau đây để thay thế. Tôi thấy hơi kỳ lạ khi bạn được phép kiểm tra xem thiết bị có Face ID hay không cho đến khi người dùng phản hồi với việc chuyển đổi và sau đó bạn thậm chí không thể kiểm tra nữa. Làm thế nào tôi có thể cung cấp một thông báo lỗi hữu ích để nói đến Cài đặt và chuyển đổi Face ID?
Jeremy

@Jeremy Tôi không sở hữu iPhone X, vì vậy tôi không biết. Có lẽ bạn có thể sử dụng mô hình phát hiện ở trên ( model == "iPhone10,3" || model == "iPhone10,6") và nếu canUseFaceIDtrả về false, điều đó có nghĩa là nó đã bị người dùng từ chối.
Cœur

1
@MateoOlaya Không có gì trong câu trả lời của tôi sẽ bị Apple từ chối: bạn có thể sử dụng tất cả.
Cœur

42

Bạn có thể làm như thế này để phát hiện thiết bị iPhone X theo kích thước.

Nhanh

if UIDevice().userInterfaceIdiom == .phone && UIScreen.main.nativeBounds.height == 2436 {
   //iPhone X
}

Mục tiêu - C

if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone && UIScreen.mainScreen.nativeBounds.size.height == 2436)  {
  //iPhone X     
}

nhập mô tả hình ảnh ở đây

Nhưng ,

Đây không phải là cách đủ. Điều gì sẽ xảy ra nếu Apple công bố iPhone tiếp theo có cùng kích thước của iPhone X. vì vậy cách tốt nhất là sử dụng chuỗi Phần cứng để phát hiện thiết bị.

Đối với thiết bị mới hơn Chuỗi phần cứng như dưới đây.

iPhone 8 - iPhone10,1 hoặc iPhone 10,4

iPhone 8 Plus - iPhone10,2 hoặc iPhone 10,5

iPhone X - iPhone10,3 hoặc iPhone10,6


2
Bạn nên sử dụng [UIDevice currentDevice]thay vì[[UIDevice alloc] init]
S. Matsepura

vấn đề duy nhất với chuỗi phần cứng là nó không hoạt động trên mô phỏng
quemeful

38

Kiểm tra kiểu máy / tên máy , KHÔNG sử dụng trực tiếp số điểm / pixel trong mã của bạn, đó là mã cứng và vô nghĩa đối với phần cứng thiết bị, kiểu thiết bị là mã định danh duy nhất cho một loại thiết bị phù hợp .

#import <sys/utsname.h>

NSString* deviceName()
{
    struct utsname systemInfo;
    uname(&systemInfo);

    return [NSString stringWithCString:systemInfo.machine
                          encoding:NSUTF8StringEncoding];
}

Kết quả:

@"iPhone10,3" on iPhone X (CDMA)
@"iPhone10,6" on iPhone X (GSM)

Tham khảo câu trả lời này .

Thực hiện mã đầy đủ:

#import <sys/utsname.h>

NSString * GetDeviceModel(void)
{
    static dispatch_once_t onceToken;
    static NSString *strModelID = nil;

    dispatch_once(&onceToken, ^{
#if TARGET_IPHONE_SIMULATOR
        strModelID = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];
#else
        struct utsname systemInfo;

        uname(&systemInfo);
        strModelID = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
#endif
    });

    return strModelID;
}

// See the `Hardware strings` in https://en.wikipedia.org/wiki/List_of_iOS_devices
BOOL IsiPhoneX(void)
{
    NSString *strModelID = GetDeviceModel();

    return [strModelID isEqualToString:@"iPhone10,3"] || [strModelID isEqualToString:@"iPhone10,6"];
}

BOOL IsNotchiPhone(void)
{
    NSArray<NSString *> *notchiModels = @[
        @"iPhone10,3", @"iPhone10,6", // iPhone X
        @"iPhone11,2", @"iPhone11,4", @"iPhone11,6", // iPhone XS (Max)
        @"iPhone11,8", // iPhone XR
        @"iPhone12,1", @"iPhone12,3", @"iPhone12,5", // iPhone 11 (Pro (Max))
    ];

    return [notchiModels containsObject:GetDeviceModel()];
}

1
Câu trả lời tuyệt vời vì nó xử lý các mô phỏng chính xác. Vui lòng thêm dòng #import vào phần "mã đầy đủ". Tôi đã bỏ lỡ điều đó (sao chép / dán) vào lần thử đầu tiên của tôi.
mpoisot

1
đó là phương pháp ưa thích của tôi Tham khảo wiki này để biết danh sách đầy đủ các chuỗi mô hình thiết bị. Là một bình luận phụ, @ "iphone10,3" cũng có thể được xem là mã cứng.
YvesLeBorg

1
@YvesLeBorg Vâng, đó thực sự là một vấn đề gây tranh cãi. Chuỗi mô hình phần cứng có một mã định danh duy nhất hơn các điểm màn hình cho thiết bị, tôi nghĩ vậy. Nói chung, nó được sử dụng để thống kê dữ liệu.
Itachi

25
#define IS_IPHONE        (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS  (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_IPHONE_X      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)

xác định IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] giới hạn] .size.height == 812.0)

#define IS_IPHONE_XS      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)
#define IS_IPHONE_X_MAX      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 896.0)
#define IS_RETINA        ([[UIScreen mainScreen] scale] >= 2.0) // 3.0 for iPhone X, 2.0 for others

#define IS_IPAD_DEVICE   [(NSString*)[UIDevice currentDevice].model hasPrefix:@"iPad"]

Lưu ý: - Hãy cẩn thận, nó chỉ hoạt động tốt cho hướng dọc


2
Hãy cẩn thận, nó chỉ hoạt động tốt đối với hướng dọc
CFIFok

1
Cảm ơn vì điều đó. Hoạt động tốt. Trong chế độ Phong cảnh, bạn cần điều chỉnh các số đó. Số ma thuật của iPhoneX ở chế độ Phong cảnh là 375.0
pvella

Có một vài iPhone Plus / Max / Pro sử dụng nativeScalecùng 3.0, phải không?
Itachi

24

Sau khi xem tất cả các câu trả lời, đây là những gì tôi đã làm:

Giải pháp (tương thích Swift 4.1)

extension UIDevice {
    static var isIphoneX: Bool {
        var modelIdentifier = ""
        if isSimulator {
            modelIdentifier = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] ?? ""
        } else {
            var size = 0
            sysctlbyname("hw.machine", nil, &size, nil, 0)
            var machine = [CChar](repeating: 0, count: size)
            sysctlbyname("hw.machine", &machine, &size, nil, 0)
            modelIdentifier = String(cString: machine)
        }

        return modelIdentifier == "iPhone10,3" || modelIdentifier == "iPhone10,6"
    }

    static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0
    }
}

Sử dụng

if UIDevice.isIphoneX {
    // is iPhoneX
} else {
    // is not iPhoneX
}

Ghi chú

Pre Swift 4.1 bạn có thể kiểm tra xem ứng dụng có chạy trên trình giả lập như vậy không:

TARGET_OS_SIMULATOR != 0

Từ Swift 4.1 trở đi, bạn có thể kiểm tra xem ứng dụng có chạy trên trình giả lập hay không bằng điều kiện nền tảng môi trường Target :

#if targetEnvironment(simulator)
    return true
#else
    return false
#endif

(phương thức cũ hơn vẫn sẽ hoạt động, nhưng phương pháp mới này là bằng chứng trong tương lai nhiều hơn)


táo sẽ ổn với điều này?
Surjeet Rajput

@ Commando24 Có, tôi không thấy bất kỳ lý do nào để họ từ chối ứng dụng vì mã này.
Cloud9999Strife

18

Tất cả các câu trả lời dựa trên kích thước này dễ bị hành vi không chính xác trên các thiết bị trong tương lai. Họ sẽ làm việc ngày hôm nay, nhưng điều gì sẽ xảy ra nếu một chiếc iPhone vào năm tới có cùng kích thước nhưng có camera, v.v ... dưới kính nên không có "notch?" Nếu lựa chọn duy nhất là cập nhật ứng dụng, thì đó là một giải pháp kém cho bạn và khách hàng của bạn.

Bạn cũng có thể kiểm tra chuỗi mô hình phần cứng như "iPhone10,1", nhưng điều đó có vấn đề vì đôi khi Apple phát hành số mô hình khác nhau cho các nhà mạng khác nhau trên khắp thế giới.

Cách tiếp cận đúng là thiết kế lại bố cục trên cùng hoặc giải quyết các vấn đề bạn gặp phải với chiều cao của thanh điều hướng tùy chỉnh (đó là điều tôi tập trung vào). Nhưng, nếu bạn quyết định không làm một trong những điều đó, hãy nhận ra rằng bất cứ điều gì bạn đang làm là hack để có được điều này ngay hôm nay , và bạn sẽ cần phải sửa nó vào một lúc nào đó, có thể nhiều lần, để giữ cho hack đang làm việc.


1
Đúng. Tinh chỉnh một giả định rằng số X sẽ luôn là A thành một mà số X sẽ luôn là A trừ khi điều kiện Y khi đó sẽ là B chỉ đào sâu hơn. Kích thước dựa trên vùng an toàn do Apple chỉ định, không phải bằng cách đoán thứ hai.
Tommy

2
Tôi sẽ lo lắng về iPhone tiếp theo khi nó thực sự ở ngoài đó. Tôi muốn ứng dụng của tôi hoạt động NGAY HÔM NAY.
Vahid Amiri

13

SWift 4+ Trả lời

iPhone X, XR, XS, XSMAX, 11 Pro, 11 Pro Max:

Lưu ý: Cần thiết bị thực để thử nghiệm

Tài liệu tham khảo

 let deviceType = UIDevice.current.modelName
        switch deviceType {
        case "iPhone10,3", "iPhone10,6":
            print("iPhoneX")
        case "iPhone11,2":
            print("iPhone XS")
        case "iPhone11,4":
            print("iPhone XS Max")
        case "iPhone11,6":
            print("iPhone XS Max China")
        case "iPhone11,8":
            print("iPhone XR")
        case "iPhone12,3":
            print("iPhone 11 Pro")
        case "iPhone12,5":
            print("iPhone 11 Pro Max")
        default:
            break
}

extension UIDevice {
    var modelName: String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }
        return identifier
    }
}

Đối với phương thức 1, bạn có thể lấy đi thuộc tính "var window" bên ngoài func và chỉ một hằng số "cho phép" bên trong nó (gõ UIWindow, tức là không bắt buộc). Tôi thích câu trả lời này vì khi khởi động, self.view.window có thể là con số không và UIApplication. Shared.keyWindow cũng có thể là con số không, trong khi việc tạo UIWindow theo cách này hoạt động mọi lúc.
Rolleric

11

SWift 4/5 tiện ích mở rộng có thể tái sử dụng với hỗ trợ iPhone 11

    public extension UIDevice {

    public enum `Type` {
        case iPad
        case iPhone_unknown
        case iPhone_5_5S_5C
        case iPhone_6_6S_7_8
        case iPhone_6_6S_7_8_PLUS
        case iPhone_X_Xs
        case iPhone_Xs_11_Pro_Max
        case iPhone_Xr_11
        case iPhone_11_Pro
    }

    public var hasHomeButton: Bool {
        switch type {
        case .iPhone_X_Xs, .iPhone_Xr_11, .iPhone_Xs_11_Pro_Max, .iPhone_11_Pro:
            return false
        default:
            return true
        }
    }

    public var type: Type {
        if userInterfaceIdiom == .phone {
            switch UIScreen.main.nativeBounds.height {
            case 1136: return .iPhone_5_5S_5C
            case 1334: return .iPhone_6_6S_7_8
            case 1920, 2208: return .iPhone_6_6S_7_8_PLUS
            case 2436: return .iPhone_X_Xs
            case 2688: return .iPhone_Xs_11_Pro_Max
            case 1792: return .iPhone_Xr_11
            case 2426: return .iPhone_11_Pro
            default: return .iPhone_unknown
        }
        }
        return .iPad
   }
}

2
tiện ích mở rộng tốt, nhưng hữu ích nhất ở đây làUIDevice.current.hasHomeButton
WINSergey

1
@ale_stro có tốt không khi sử dụng userInterfaceIdiom để xác định thiết bị cho ứng dụng phổ quát? hầu hết mọi người không khuyên điều này. Có hại gì khi sử dụng không?
shaqir sai

10

Vâng, nó là có thể. Tải xuống tiện ích mở rộng UIDevice-Phần cứng (hoặc cài đặt qua Cốc Cốc 'UIDevice-Phần cứng') rồi sử dụng:

NSString* modelID = [[[UIDevice currentDevice] modelIdentifier];
BOOL isIphoneX = [modelID isEqualToString:@"iPhone10,3"] || [modelID isEqualToString:@"iPhone10,6"];

Lưu ý rằng điều này sẽ không hoạt động trong Trình mô phỏng, chỉ trên thiết bị thực tế.


Tất cả mã thiết bị tại đây: iphonesoft.fr/2016/10/31/ Ví dụ: iPhone X: iPhone10,5 và iPhone10,6
Medhi

Chuỗi Phần cứng từ wikipedia cho biết "iPhone10,3 và iPhone10,6". @Medhi
Itachi

@Medhi, bạn có thể sử dụng ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]trong Trình mô phỏng để lấy các giá trị thực từ Xcode.
Cœur

9

Theo phản hồi của @ saswanb, đây là phiên bản Swift 4:

var iphoneX = false
if #available(iOS 11.0, *) {
    if ((UIApplication.shared.keyWindow?.safeAreaInsets.top)! > CGFloat(0.0)) {
        iphoneX = true
    }
}

Thanh trạng thái cũng được xem xét bên ngoài khu vực an toàn! Vì vậy, điều này sẽ trả lại dương tính giả! Nó phải cao hơn 20 điểm (chiều cao của thanh trạng thái). Điều này cũng trả về đúng nếu thiết bị là iPhone Xs, R hoặc Xs Max.
MQoder

đang hoạt động tuyệt vời, nhưng hãy cẩn thận: keyWindownilcho đến khi bộ điều khiển giao diện chính đã kêu gọiviewDidAppear
Casey

9

Tôi biết đó chỉ là một Swift giải pháp , nhưng nó có thể giúp được ai đó.

Tôi có globals.swifttrong mọi dự án và một trong những điều tôi luôn thêm vào là DeviceTypedễ dàng phát hiện thiết bị của người dùng:

struct ScreenSize {
  static let width = UIScreen.main.bounds.size.width
  static let height = UIScreen.main.bounds.size.height
  static let frame = CGRect(x: 0, y: 0, width: ScreenSize.width, height: ScreenSize.height)
  static let maxWH = max(ScreenSize.width, ScreenSize.height)
}

struct DeviceType {
  static let iPhone4orLess = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH < 568.0
  static let iPhone5orSE   = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 568.0
  static let iPhone678     = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 667.0
  static let iPhone678p    = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 736.0
  static let iPhoneX       = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 812.0
  static let iPhoneXRMax   = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 896.0
  static var hasNotch: Bool {
    return iPhoneX || iPhoneXRMax
  }
}

Sau đó, để sử dụng nó:

if DeviceType.hasNotch {
  print("This executes on all phones with a notch")
}

if DeviceType.iPhone678 {
  print("This executes on iPhones 6, 7 and 8")
}

Nếu bạn sử dụng LaunchImagetrong dự án của mình, hãy đảm bảo thêm hình ảnh cho tất cả các thiết bị được hỗ trợ (như XS Max, XR) vì UIScreen.main.boundssẽ không trả lại giá trị phù hợp nếu không có những thiết bị đó.


1
Người bạn mới quen với Swift đã hỏi cách sử dụng cái này, chỉ trong trường hợp người khác không biết if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling

5

Tất cả các câu trả lời đang sử dụng heightchỉ là một nửa của câu chuyện vì một lý do. Nếu bạn sẽ kiểm tra như vậy khi định hướng thiết bị landscapeLefthoặc landscapeRightkiểm tra sẽ thất bại, vì heightnó bị tráo đổi vớiwidth .

Đó là lý do giải pháp của tôi giống như thế này trong Swift 4.0:

extension UIScreen {
    ///
    static var isPhoneX: Bool {
        let screenSize = UIScreen.main.bounds.size
        let width = screenSize.width
        let height = screenSize.height
        return min(width, height) == 375 && max(width, height) == 812
    }
}

Chỉ cần sử dụng localBound thay thế
Leo Dabus

4

Bạn không nên cho rằng thiết bị duy nhất mà Apple phát hành có chiều cao UINavestionBar khác sẽ là iPhone X. Hãy thử giải quyết vấn đề này bằng giải pháp chung hơn. Nếu bạn muốn thanh luôn lớn hơn 20px so với chiều cao mặc định của nó, mã của bạn nên thêm 20px vào chiều cao của thanh, thay vì đặt nó thành 64px (44px + 20px).


Vì vậy, những gì giải pháp khác bạn phải đề xuất?
Stephane Mathis

@xaphod có câu trả lời tốt hơn bây giờ.
Cœur

4
struct ScreenSize {
    static let width = UIScreen.main.bounds.size.width
    static let height = UIScreen.main.bounds.size.height
    static let maxLength = max(ScreenSize.width, ScreenSize.height)
    static let minLength = min(ScreenSize.width, ScreenSize.height)
    static let frame = CGRect(x: 0, y: 0, width: ScreenSize.width, height: ScreenSize.height)
}

struct DeviceType {
    static let iPhone4orLess = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength < 568.0
    static let iPhone5orSE = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 568.0
    static let iPhone678 = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 667.0
    static let iPhone678p = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 736.0
    static let iPhoneX = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 812.0

    static let IS_IPAD              = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1024.0
    static let IS_IPAD_PRO          = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1366.0
}

4

Swift 3 + 4:

không cần bất kỳ giá trị pixel kích thước thiết bị

//UIApplication+SafeArea.swift

extension UIApplication { 

    static var isDeviceWithSafeArea:Bool {

        if #available(iOS 11.0, *) {
            if let topPadding = shared.keyWindow?.safeAreaInsets.bottom,
                topPadding > 0 {
                return true
            }
        }

        return false
    }
}

Thí dụ:

if UIApplication.isDeviceWithSafeArea {
     //e.g. change the frame size height of your UITabBar
}

3
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)

2
nó sẽ trả về cho bạn 812 nếu bạn tải lên Hình ảnh mặc định cho iPhone X. Cho đến khi tôi nghĩ rằng nó sẽ trả lại cho bạn kích thước iPhone 7, mặc dù không chắc chắn ...
Fahim Parkar

3
- (BOOL)isIphoneX {
    if (@available(iOS 11.0, *)) {
        UIWindow *window = UIApplication.sharedApplication.keyWindow;
        CGFloat topPadding = window.safeAreaInsets.top;
        if(topPadding>0) {
            return YES;
        }
        else {
            return NO;
        }
    }
    else {
        return NO;
    }
}

1
Câu trả lời tốt nhất! Không cần bất kỳ giá trị pixel kích thước thiết bị.
Peter Kreinz

3

Thông thường, Lập trình viên cần nó để ràng buộc lên trên hoặc dưới cùng, vì vậy các phương thức này có thể giúp

static func extraTop() -> CGFloat {

    var top: CGFloat = 0

    if #available(iOS 11.0, *) {

        if let t = UIApplication.shared.keyWindow?.safeAreaInsets.top {
            top = t
        }
    }
    return top
}

static func extraBottom() -> CGFloat {

    var bottom: CGFloat = 0

    if #available(iOS 11.0, *) {

        if let b = UIApplication.shared.keyWindow?.safeAreaInsets.bottom {
            bottom = b
        }
    }
    return bottom
}

Đối với trước iPhone X, các phương thức này trả về: 0

Đối với iPhone X: 44 và 34 tương ứng

Sau đó, chỉ cần thêm các tính năng bổ sung này vào các ràng buộc trên hoặc dưới


3

Đối với những người nhận được 2001px thay vì 2436px cho chiều cao giới hạn gốc (như tôi), đó là do bạn đã xây dựng ứng dụng của mình bằng SDK cũ hơn, trước iOS 11 (Xcode 8 thay vì Xcode 9). Với SDK cũ hơn, iOS sẽ hiển thị các ứng dụng "hộp đen" trên iPhone X thay vì mở rộng cạnh màn hình, ngoài "notch cảm biến" hàng đầu. Điều này làm giảm kích thước màn hình, đó là lý do tại sao tài sản đó trả về 2001 thay vì 2436.

Giải pháp đơn giản nhất là chỉ kiểm tra cả hai kích thước nếu bạn chỉ quan tâm đến việc phát hiện thiết bị. Tôi đã sử dụng phương pháp này để phát hiện FaceID trong khi xây dựng với SDK Xcode cũ hơn không có giá trị ENUM chỉ định loại sinh trắc học. Trong tình huống này, phát hiện thiết bị sử dụng chiều cao màn hình có vẻ là cách tốt nhất để biết liệu thiết bị có FaceID vs TouchID mà không phải cập nhật Xcode hay không.


3

KHÔNG sử dụng kích thước pixel màn hình như các giải pháp khác đã đề xuất, điều này rất tệ vì nó có thể dẫn đến kết quả dương tính giả cho các thiết bị trong tương lai; sẽ không hoạt động nếu UIWindow chưa được kết xuất (AppDelegate), sẽ không hoạt động trong các ứng dụng ngang và có thể thất bại trên trình giả lập nếu đặt tỷ lệ.

Thay vào đó, tôi đã tạo ra một macro cho mục đích này, nó rất dễ sử dụng và dựa vào các cờ phần cứng để ngăn chặn các vấn đề đã nói ở trên.

Chỉnh sửa: Đã cập nhật để hỗ trợ iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Max


Để sử dụng:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

Yup, thực sự.


Macro:

Chỉ cần sao chép dán ở bất cứ đâu, tôi thích phần dưới cùng của tệp .h của mình sau @end

#import <sys/utsname.h>

#if TARGET_IPHONE_SIMULATOR
#define IS_SIMULATOR YES
#else
#define IS_SIMULATOR NO
#endif

#define IS_DEVICE_IPHONEX (\
(^BOOL (void){\
NSString *__modelIdentifier;\
if (IS_SIMULATOR) {\
__modelIdentifier = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];\
} else {\
struct utsname __systemInfo;\
uname(&__systemInfo);\
__modelIdentifier = [NSString stringWithCString:__systemInfo.machine encoding:NSUTF8StringEncoding];\
}\
NSString *__iPhoneX_GSM_Identifier = @"iPhone10,6";\
NSString *__iPhoneX_CDMA_Identifier = @"iPhone10,3";\
NSString *__iPhoneXR_Identifier = @"iPhone11,8";\
NSString *__iPhoneXS_Identifier = @"iPhone11,2";\
NSString *__iPhoneXSMax_China_Identifier = @"iPhone11,6";\
NSString *__iPhoneXSMax_Other_Identifier = @"iPhone11,4";\
return ([__modelIdentifier isEqualToString:__iPhoneX_GSM_Identifier] || [__modelIdentifier isEqualToString:__iPhoneX_CDMA_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXR_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXS_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXSMax_China_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXSMax_Other_Identifier]);\
})()\
)

Lý do duy nhất tôi có thể nghĩ để phát hiện iPhoneX là để tránh phần notch ở phía trên màn hình; nếu vậy bạn có thể kiểm tra safeArea.top để phát hiện kích thước của notch đã nói. Chỉ cần đảm bảo rằng bạn đo nó sau khi UIWindow đã tải, vì vậy không phải trong viewDidLoad mà là một chu trình sau:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw

2

Tôi đã xây dựng các câu trả lời của bạn cho câu trả lời của người khác và thực hiện mở rộng nhanh chóng trên UIDevice. Tôi thích enum nhanh chóng và "mọi thứ theo thứ tự" & nguyên tử hóa. Tôi đã tạo giải pháp hoạt động cả trên thiết bị và trình giả lập.

Ưu điểm: - giao diện đơn giản, cách sử dụng, ví dụ UIDevice.current.isIPhoneX -UIDeviceModelType enum cung cấp cho bạn khả năng dễ dàng mở rộng các tính năng và hằng số cụ thể của mô hình mà bạn muốn sử dụng trong ứng dụng của mình, ví dụ như angleRadius

Nhược điểm: - đó là giải pháp cụ thể theo mô hình, không phải độ phân giải cụ thể - ví dụ: nếu Apple sẽ sản xuất một mô hình khác có cùng thông số kỹ thuật, điều này sẽ không hoạt động chính xác và bạn cần thêm một mô hình khác để hoạt động này => bạn cần cập nhật ứng dụng.

extension UIDevice {

    enum UIDeviceModelType : Equatable {

        ///iPhoneX
        case iPhoneX

        ///Other models
        case other(model: String)

        static func type(from model: String) -> UIDeviceModelType {
            switch model {
            case "iPhone10,3", "iPhone10,6":
                return .iPhoneX
            default:
                return .other(model: model)
            }
        }

        static func ==(lhs: UIDeviceModelType, rhs: UIDeviceModelType) -> Bool {
            switch (lhs, rhs) {
            case (.iPhoneX, .iPhoneX):
                return true
            case (.other(let modelOne), .other(let modelTwo)):
                return modelOne == modelTwo
            default:
                return false
            }
        }
    }

    var simulatorModel: String? {
        guard TARGET_OS_SIMULATOR != 0 else {
            return nil
        }

        return ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"]
    }

    var hardwareModel: String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let model = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }

        return model
    }

    var modelType: UIDeviceModelType {
        let model = self.simulatorModel ?? self.hardwareModel
        return UIDeviceModelType.type(from: model)
    }

    var isIPhoneX: Bool {
        return modelType == .iPhoneX
    }
}

Thay vì sử dụng Mirror, nó sẽ nhanh hơn để sử dụng sysctlbynamenhư được thực hiện trong câu trả lời của Cloud9999Strife (và trong câu trả lời của tôi cũng vậy).
Cœur

2

Tôi dựa vào chiều cao Khung trạng thái để phát hiện xem đó có phải là iPhone X không:

if UIApplication.shared.statusBarFrame.height >= CGFloat(44) {
    // It is an iPhone X
}

Đây là cho ứng dụng un chân dung. Bạn cũng có thể kiểm tra kích thước theo hướng thiết bị. Ngoài ra, trên các iPhone khác, Thanh trạng thái có thể bị ẩn, do đó chiều cao khung hình là 0. Trên iPhone X, Status Bar không bao giờ bị ẩn.


Bạn có thể ẩn trạng thái iPhoneX controllerbằng cách này: - (BOOL)prefersStatusBarHidden { return YES; } Sau đó, chiều cao của statusBar là 0.
夜 星辰

@ 无 夜 之 Tôi kiểm tra điều này khi khởi động trong AppDelegate.
Tiois

2

Tôi đã sử dụng mã của Peter Kreinz (vì nó sạch và đã làm những gì tôi cần) nhưng sau đó tôi nhận ra nó hoạt động ngay khi thiết bị ở chế độ dọc (vì phần đệm trên cùng sẽ ở trên cùng, rõ ràng) Vì vậy, tôi đã tạo một tiện ích mở rộng để xử lý tất cả định hướng với phần đệm tương ứng của nó, mà không chuyển tiếp trên kích thước màn hình:

extension UIDevice {

    var isIphoneX: Bool {
        if #available(iOS 11.0, *), isIphone {
            if isLandscape {
                if let leftPadding = UIApplication.shared.keyWindow?.safeAreaInsets.left, leftPadding > 0 {
                    return true
                }
                if let rightPadding = UIApplication.shared.keyWindow?.safeAreaInsets.right, rightPadding > 0 {
                    return true
                }
            } else {
                if let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, topPadding > 0 {
                    return true
                }
                if let bottomPadding = UIApplication.shared.keyWindow?.safeAreaInsets.bottom, bottomPadding > 0 {
                    return true
                }
            }
        }
        return false
    }

    var isLandscape: Bool {
        return UIDeviceOrientationIsLandscape(orientation) || UIInterfaceOrientationIsLandscape(UIApplication.shared.statusBarOrientation)
    }

    var isPortrait: Bool {
        return UIDeviceOrientationIsPortrait(orientation) || UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)
    }

    var isIphone: Bool {
        return self.userInterfaceIdiom == .phone
    }

    var isIpad: Bool {
        return self.userInterfaceIdiom == .pad
    }
}

Và trên trang web cuộc gọi của bạn, bạn chỉ cần:

let res = UIDevice.current.isIphoneX

2

Ngoài ra, bạn có thể kiểm tra pod ' DeviceKit '. Sau khi cài đặt, tất cả những gì bạn cần làm để kiểm tra thiết bị là:

import DeviceKit
let device = Device()
if device == .iPhoneX {
  // place your code here
}

2

Tháng 11 năm 2019:

Đây là những gì tôi sử dụng trong tất cả các dự án sản xuất của tôi. Lưu ý rằng ý chính này khá dài.

  1. Điều này không sử dụng các tính toán về chiều rộng hoặc chiều cao, mà là:
  2. Nó kiểm tra mô hình chuỗi thiết bị.
  3. Không có nguy cơ khiến bản dựng của bạn bị Apple từ chối vì sử dụng bất kỳ API riêng tư / không có giấy tờ nào.
  4. Hoạt động với trình mô phỏng

    import UIKit
    
    class DeviceUtility {
        /// Determines if the current device of the user is an iPhoneX type/variant.
        static var isIphoneXType: Bool {
            get {
                switch UIDevice().type {
                case .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhoneX, .iPhone11, .iPhone11Pro, .iPhone11ProMax: return true
                default: return false
                }
            }
        }
    }
    
    
    public enum DeviceModel : String {
        case simulator     = "simulator/sandbox",
    
        // MARK: - iPods
    
        iPod1              = "iPod 1",
        iPod2              = "iPod 2",
        iPod3              = "iPod 3",
        iPod4              = "iPod 4",
        iPod5              = "iPod 5",
    
        // MARK: - iPads
    
        iPad2              = "iPad 2",
        iPad3              = "iPad 3",
        iPad4              = "iPad 4",
        iPadAir            = "iPad Air ",
        iPadAir2           = "iPad Air 2",
        iPad5              = "iPad 5", //aka iPad 2017
        iPad6              = "iPad 6", //aka iPad 2018
    
        // MARK: - iPad Minis
    
        iPadMini           = "iPad Mini",
        iPadMini2          = "iPad Mini 2",
        iPadMini3          = "iPad Mini 3",
        iPadMini4          = "iPad Mini 4",
    
        // MARK: - iPad Pros
    
        iPadPro9_7         = "iPad Pro 9.7\"",
        iPadPro10_5        = "iPad Pro 10.5\"",
        iPadPro12_9        = "iPad Pro 12.9\"",
        iPadPro2_12_9      = "iPad Pro 2 12.9\"",
    
        // MARK: - iPhones
    
        iPhone4            = "iPhone 4",
        iPhone4S           = "iPhone 4S",
        iPhone5            = "iPhone 5",
        iPhone5S           = "iPhone 5S",
        iPhone5C           = "iPhone 5C",
        iPhone6            = "iPhone 6",
        iPhone6plus        = "iPhone 6 Plus",
        iPhone6S           = "iPhone 6S",
        iPhone6Splus       = "iPhone 6S Plus",
        iPhoneSE           = "iPhone SE",
        iPhone7            = "iPhone 7",
        iPhone7plus        = "iPhone 7 Plus",
        iPhone8            = "iPhone 8",
        iPhone8plus        = "iPhone 8 Plus",
        iPhoneX            = "iPhone X",
        iPhoneXS           = "iPhone XS",
        iPhoneXSMax        = "iPhone XS Max",
        iPhoneXR           = "iPhone XR",
        iPhone11           = "iPhone 11",
        iPhone11Pro        = "iPhone 11 Pro",
        iPhone11ProMax     = "iPhone 11 Pro Max",
    
        // MARK: - Apple TVs
    
        AppleTV            = "Apple TV",
        AppleTV_4K         = "Apple TV 4K",
    
        // MARK: - Unrecognized
    
        unrecognized       = "?unrecognized?"
    }
    
    // #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    //MARK: UIDevice extensions
    // #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    
    public extension UIDevice {
        var type: DeviceModel {
            var systemInfo = utsname()
            uname(&systemInfo)
            let modelCode = withUnsafePointer(to: &systemInfo.machine) {
                $0.withMemoryRebound(to: CChar.self, capacity: 1) {
                    ptr in String.init(validatingUTF8: ptr)
    
                }
            }
            let modelMap : [ String : DeviceModel ] = [
    
                // MARK: - Simulators
    
                "i386"      : .simulator,
                "x86_64"    : .simulator,
    
                // MARK: - iPod
    
                "iPod1,1"   : .iPod1,
                "iPod2,1"   : .iPod2,
                "iPod3,1"   : .iPod3,
                "iPod4,1"   : .iPod4,
                "iPod5,1"   : .iPod5,
    
                // MARK: - iPad
    
                "iPad2,1"   : .iPad2,
                "iPad2,2"   : .iPad2,
                "iPad2,3"   : .iPad2,
                "iPad2,4"   : .iPad2,
                "iPad3,1"   : .iPad3,
                "iPad3,2"   : .iPad3,
                "iPad3,3"   : .iPad3,
                "iPad3,4"   : .iPad4,
                "iPad3,5"   : .iPad4,
                "iPad3,6"   : .iPad4,
                "iPad4,1"   : .iPadAir,
                "iPad4,2"   : .iPadAir,
                "iPad4,3"   : .iPadAir,
                "iPad5,3"   : .iPadAir2,
                "iPad5,4"   : .iPadAir2,
                "iPad6,11"  : .iPad5, //aka iPad 2017
                "iPad6,12"  : .iPad5,
                "iPad7,5"   : .iPad6, //aka iPad 2018
                "iPad7,6"   : .iPad6,
    
                // MARK: - iPad mini
    
                "iPad2,5"   : .iPadMini,
                "iPad2,6"   : .iPadMini,
                "iPad2,7"   : .iPadMini,
                "iPad4,4"   : .iPadMini2,
                "iPad4,5"   : .iPadMini2,
                "iPad4,6"   : .iPadMini2,
                "iPad4,7"   : .iPadMini3,
                "iPad4,8"   : .iPadMini3,
                "iPad4,9"   : .iPadMini3,
                "iPad5,1"   : .iPadMini4,
                "iPad5,2"   : .iPadMini4,
    
                // MARK: - iPad pro
    
                "iPad6,3"   : .iPadPro9_7,
                "iPad6,4"   : .iPadPro9_7,
                "iPad7,3"   : .iPadPro10_5,
                "iPad7,4"   : .iPadPro10_5,
                "iPad6,7"   : .iPadPro12_9,
                "iPad6,8"   : .iPadPro12_9,
                "iPad7,1"   : .iPadPro2_12_9,
                "iPad7,2"   : .iPadPro2_12_9,
    
                // MARK: - iPhone
    
                "iPhone3,1" : .iPhone4,
                "iPhone3,2" : .iPhone4,
                "iPhone3,3" : .iPhone4,
                "iPhone4,1" : .iPhone4S,
                "iPhone5,1" : .iPhone5,
                "iPhone5,2" : .iPhone5,
                "iPhone5,3" : .iPhone5C,
                "iPhone5,4" : .iPhone5C,
                "iPhone6,1" : .iPhone5S,
                "iPhone6,2" : .iPhone5S,
                "iPhone7,1" : .iPhone6plus,
                "iPhone7,2" : .iPhone6,
                "iPhone8,1" : .iPhone6S,
                "iPhone8,2" : .iPhone6Splus,
                "iPhone8,4" : .iPhoneSE,
                "iPhone9,1" : .iPhone7,
                "iPhone9,3" : .iPhone7,
                "iPhone9,2" : .iPhone7plus,
                "iPhone9,4" : .iPhone7plus,
                "iPhone10,1" : .iPhone8,
                "iPhone10,4" : .iPhone8,
                "iPhone10,2" : .iPhone8plus,
                "iPhone10,5" : .iPhone8plus,
                "iPhone10,3" : .iPhoneX,
                "iPhone10,6" : .iPhoneX,
                "iPhone11,2" : .iPhoneXS,
                "iPhone11,4" : .iPhoneXSMax,
                "iPhone11,6" : .iPhoneXSMax,
                "iPhone11,8" : .iPhoneXR,
                "iPhone12,1" : .iPhone11,
                "iPhone12,3" : .iPhone11Pro,
                "iPhone12,5" : .iPhone11ProMax,
    
                // MARK: - AppleTV
    
                "AppleTV5,3" : .AppleTV,
                "AppleTV6,2" : .AppleTV_4K
            ]
    
            if let model = modelMap[String.init(validatingUTF8: modelCode!)!] {
                if model == .simulator {
                    if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
                        if let simModel = modelMap[String.init(validatingUTF8: simModelCode)!] {
                            return simModel
                        }
                    }
                }
                return model
            }
            return DeviceModel.unrecognized
        }
    }

Cách sử dụng: hãy chèn: CGFloat = DeviceUtility.isIphoneXType? 50,0: 40,0


Hoạt động hoàn hảo. Cảm ơn. Tôi đang sử dụng nó trong một dự án SwiftUI.
LondonGuy

1

Tôi đã phải giải quyết vấn đề tương tự gần đây. Và trong khi câu hỏi này được trả lời dứt khoát ("Không"), điều này có thể giúp những người khác cần hành vi bố cục cụ thể của iPhone X.

Tôi không thực sự quan tâm đến việc thiết bị có phải là iPhone X hay không. Tôi quan tâm đến việc thiết bị có màn hình không.

private static var hasNotchedDisplay: Bool {
    if let window = UIApplication.shared.keyWindow {
        return (window.compatibleSafeAreaInsets.top > 20.0 || window.compatibleSafeAreaInsets.left > 0.0 || window.compatibleSafeAreaInsets.right > 0.0)
    }

    return false
}

Bạn cũng có thể viết một hasOnScreenHomeIndicatorbiến dọc theo cùng một dòng (mặc dù có thể kiểm tra vùng an toàn dưới cùng, có thể không?).

Ở trên sử dụng tiện ích mở rộng của tôi UIViewđể truy cập thuận tiện vào các vùng an toàn trong iOS 10 trở về trước.

@objc public extension UIView {
    @objc public var compatibleSafeAreaInsets: UIEdgeInsets {
        if #available(iOS 11.0, *) {
            return safeAreaInsets
        } else {
            return .zero
        }
    }

    @objc public var compatibleSafeAreaLayoutGuide: UILayoutGuide {
        if #available(iOS 11.0, *) {
            return safeAreaLayoutGuide
        } else {
            return layoutMarginsGuide
        }
    }
}

1

Trong Portrait chỉ tôi sử dụng chiều rộng và chiều cao của khung nhìn để kiểm tra:

override func viewDidLoad() {
    super.viewDidLoad()

    // iPhone Xr: -414 x 896
    // iPhone Xs Max: -414 x 896
    // iPhone X, Xs: -375 x 812

    if view.frame.width == 414 && view.frame.height == 896 || view.frame.width == 375 && view.frame.height == 812  {

        print("iPhone X")
    } else {

        print("not iPhone X")
    }

}

Kích thước màn hình dọc được liệt kê ở đây

nhập mô tả hình ảnh ở đây


0

Có một số lý do để muốn biết thiết bị là gì.

  1. Bạn có thể kiểm tra chiều cao của thiết bị (và chiều rộng). Điều này hữu ích cho bố cục, nhưng bạn thường không muốn làm điều đó nếu bạn muốn biết chính xác thiết bị.

  2. Đối với mục đích bố trí, bạn cũng có thể sử dụng UIView.safeAreaInsets.

  3. Ví dụ: nếu bạn muốn hiển thị tên thiết bị để được đưa vào email cho mục đích chẩn đoán, sau khi truy xuất mô hình thiết bị bằng cách sử dụng sysctl (), bạn có thể sử dụng tương đương với tên này để tìm tên:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.