ตรวจสอบว่าอุปกรณ์เป็น iPhone X


262

แอพ iOS ของฉันใช้ความสูงที่กำหนดเองUINavigationBarซึ่งนำไปสู่ปัญหาบางอย่างใน iPhone X ใหม่

มีคนรู้วิธีตรวจสอบความน่าเชื่อถือทางโปรแกรม (ใน Objective-C) แล้วหรือไม่หากแอปทำงานบน iPhone X

แก้ไข:

แน่นอนการตรวจสอบขนาดของหน้าจอเป็นไปได้ แต่ฉันสงสัยว่ามีบางวิธี "สร้างใน" ต้องการTARGET_OS_IPHONEตรวจจับ iOS ...

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

แก้ไข 2:

ฉันไม่คิดว่าคำถามของฉันซ้ำกับคำถามที่เชื่อมโยง แน่นอนว่ามีวิธีการ "วัด" คุณสมบัติต่าง ๆ ของอุปกรณ์ปัจจุบันและใช้ผลลัพธ์ในการตัดสินใจว่าจะใช้อุปกรณ์ใด อย่างไรก็ตามนี่ไม่ใช่จุดที่แท้จริงของคำถามของฉันเพราะฉันพยายามเน้นในการแก้ไขครั้งแรกของฉัน

คำถามที่แท้จริงคือ: "เป็นไปได้หรือไม่ที่จะตรวจสอบโดยตรงว่าอุปกรณ์ปัจจุบันเป็น iPhone X (เช่นโดยคุณสมบัติ SDK บางอย่าง) หรือฉันต้องใช้การวัดทางอ้อม" ?

จากคำตอบที่ให้มาฉันคิดว่าคำตอบคือ "ไม่ไม่มีวิธีการโดยตรงการวัดเป็นวิธีที่จะไป"


iPhone X มีความละเอียดหน้าจอที่แตกต่างจากคนอื่น ๆ
El Tomato

2
ใช่ฉันได้กล่าวถึงในการแก้ไขของฉันเป็นไปได้ที่จะตรวจสอบขนาดหน้าจอ แต่คำถามคือถ้ามีวิธีการ "โดยตรง" เพื่อสอบถามประเภทอุปกรณ์มากกว่า "อ้อม" วัด ...
อังเดร Herford

3
ผู้เขียนเพียงต้องการได้รับประเภทอุปกรณ์ไม่ใช่ความละเอียดหน้าจอ ทำไมไม่ตรวจสอบชื่อเครื่องโดยตรง? @lubilis ถูกต้อง
Itachi

2
ทำไมคุณไม่ลองใช้คำแนะนำเรื่องความปลอดภัยเพราะ Apple แนะนำ
holex

4
สำคัญ, devs ในอนาคต: อย่าตรวจสอบความสูงของหน้าจอที่ใช้ประโยชน์จากวิธีแก้ปัญหายอดนิยมปัจจุบันที่แนะนำว่าไม่ดีเพราะอาจส่งผลให้เกิดผลบวกปลอมสำหรับอุปกรณ์ในอนาคต จะไม่ทำงานหาก UIWindow ยังไม่ได้แสดงผล (เช่นในฟังก์ชั่น AppDelegate init ของคุณ) จะไม่ทำงานในแอพแนวนอนและอาจล้มเหลวในการจำลองหากตั้งค่ามาตราส่วน ไม่เคยใช้ตัวเลขเวทย์มนตร์สำหรับสิ่งนี้! คุณสามารถตรวจสอบการตั้งค่าสถานะฮาร์ดแวร์เพื่อรับประกันความสำเร็จอย่างที่ฉันเคยทำที่นี่: stackoverflow.com/a/51511947/2057171
Albert Renshaw

คำตอบ:


383

ตามคำถามของคุณคำตอบคือไม่ ไม่มีวิธีการโดยตรง สำหรับข้อมูลเพิ่มเติมคุณสามารถรับข้อมูลได้ที่นี่:

และ

ความสูงของ iPhone X คือ 2436 พิกเซล

จากขนาดหน้าจออุปกรณ์และความละเอียด :

ป้อนคำอธิบายรูปภาพที่นี่

จากขนาดและทิศทางของหน้าจออุปกรณ์ :

ป้อนคำอธิบายรูปภาพที่นี่

Swift 3 และใหม่กว่า :

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")
        }
    }

วัตถุประสงค์ -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");
    }
}

ตามคำถามของคุณดังนี้:

หรือใช้screenSize.heightเป็นลอย812.0f ไม่ 812int

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");
    }

สำหรับข้อมูลเพิ่มเติมคุณสามารถดูหน้าต่อไปนี้ได้ในแนวทาง iOS Human Interface Guidelines:

รวดเร็ว :

ตรวจสอบด้วยtopNotch:

หากใครที่กำลังพิจารณาใช้รอยเพื่อตรวจจับ iPhoneX โปรดจำไว้ว่าใน "แนวนอน" นั้นเหมือนกันกับ 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
}

วัตถุประสงค์ -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;
}

อัปเดต :

ห้ามใช้userInterfaceIdiomคุณสมบัติเพื่อระบุประเภทอุปกรณ์ตามเอกสารสำหรับ userInterfaceIdiomอธิบาย:

สำหรับแอปพลิเคชันสากลคุณสามารถใช้คุณสมบัตินี้เพื่อปรับแต่งลักษณะการทำงานของแอปพลิเคชันของคุณสำหรับอุปกรณ์ประเภทใดประเภทหนึ่งโดยเฉพาะ ตัวอย่างเช่นอุปกรณ์ iPhone และ iPad มีขนาดหน้าจอที่แตกต่างกันดังนั้นคุณอาจต้องการสร้างมุมมองและตัวควบคุมที่แตกต่างกันตามประเภทของอุปกรณ์ปัจจุบัน

นั่นคือคุณสมบัตินี้ใช้เพื่อระบุสไตล์การดูของแอปที่กำลังรันอยู่ อย่างไรก็ตามแอป iPhone (ไม่ใช่ Universal) สามารถติดตั้งในอุปกรณ์ iPad ผ่านทาง App Store ในกรณีนั้นuserInterfaceIdiomจะส่งคืนUIUserInterfaceIdiomPhoneมากเกินไป

unameวิธีการที่เหมาะสมที่จะได้รับชื่อเครื่องผ่าน ตรวจสอบรายละเอียดต่อไปนี้:


ความละเอียดของ iPhone X คือ 2436 x 1125 พิกเซลตาม: iphonesoft.fr/2017/09/12/ …
Medhi

1
@Medhi - ความละเอียดของ iphone X คือ - 1125 x 2436 พิกเซล (ความหนาแน่นของพิกเซล 458 ppi)
Anbu.Karthik

14
NO! สามารถติดตั้งแอป iPhone (ไม่ใช่จักรวาล) ในอุปกรณ์ iPad ผ่านทาง App Store ในกรณีนั้นuserInterfaceIdiomจะส่งคืนUIUserInterfaceIdiomPhoneเช่นกัน คำตอบนี้ผิด
Itachi

1
@ThreeCoins โปรดอัปเดตคำตอบสำหรับอุปกรณ์เพิ่มเติมตามคำแนะนำของ Leo Dabus มันใช้งานได้กับ Plus simulator แต่ไม่ใช่ในอุปกรณ์
Hiren Gujarati

2
สิ่งนี้ไม่ดีเนื่องจากอาจส่งผลในแง่บวกเท็จสำหรับอุปกรณ์ในอนาคต จะไม่ทำงานหาก UIWindow ยังไม่ได้แสดงผล (AppDelegate) จะไม่ทำงานในแอพแนวนอนและอาจล้มเหลวในการจำลองหากตั้งค่ามาตราส่วน คุณสามารถตรวจสอบการตั้งค่าสถานะฮาร์ดแวร์เพื่อรับประกันความสำเร็จอย่างที่ฉันเคยทำที่นี่: stackoverflow.com/a/51511947/2057171
Albert Renshaw

101

ความเป็นไปได้อีกอย่างซึ่งทำงานบน iOS 11 และ iOS 12 เพราะ iPhone X เป็นเพียงตัวเดียวที่มีรอยบากที่ด้านบนและสิ่งที่ใส่เข้าไป 44 นั่นคือสิ่งที่ฉันกำลังตรวจจับอยู่ที่นี่จริง ๆ :

Objective-C:

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

สวิฟท์ 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
}

และแน่นอนคุณอาจต้องตรวจสอบบริเวณที่ปลอดภัยด้านซ้ายและด้านขวาถ้าคุณอยู่ในแนวนอน

แก้ไข: _window เป็น UIWindow ของ AppDelegate ซึ่งการตรวจสอบนี้เสร็จสิ้นในแอปพลิเคชัน didFinishLaunchingWithOptions

คำตอบได้รับการอัปเดตสำหรับ iOS 12 เพื่อตรวจสอบว่าสูงสุด> 24 มากกว่าด้านบน> 0 หรือไม่

แก้ไข: ในเครื่องจำลองคุณสามารถไปที่ฮาร์ดแวร์สลับแถบสถานะการโทร การทำเช่นนั้นแสดงให้ฉันเห็นว่าความสูงของแถบสถานะไม่เปลี่ยนแปลงบน iPhone X บน iOS 11 หรือ iPhone XS iOS 12 เมื่อเข้าร่วมการโทร การเปลี่ยนแปลงทั้งหมดนั้นคือไอคอนเวลาซึ่งได้รับพื้นหลังสีเขียวในทั้งสองกรณี นี่คือ snap:

ป้อนคำอธิบายรูปภาพที่นี่


5
ส่วนที่ปลอดภัยจะมีความสูงของแถบสถานะหากมองเห็นได้บนอุปกรณ์อื่น การตรวจสอบว่าเป็น 0 จะบอกให้คุณทราบหรือไม่ว่าแถบสถานะนั้นปรากฏขึ้นไม่ใช่ว่าอุปกรณ์เป็น iPhone X หรือไม่
IMcD23

3
"สิ่งนี้อาจแตกในiPhone Xs หรือ iPhone 11 " Cook กล่าว
Itachi

11
ฉันปรับตัวเล็กน้อยและใช้if _window.safeAreaInsets != UIEdgeInsets.zeroเพื่ออนุญาตการวางแนวอุปกรณ์ใด ๆ
Fraser

2
หากคุณไม่ต้องการที่จะใช้.top, safeAreaInsets.bottomจะ 34 บน iPhone X และ 0 บนอุปกรณ์อื่น ๆ
blwinters

7
คำเตือน: อย่าใช้สิ่งนี้มันจะแตกบน iOS 12 และยังไม่มีการบันทึกว่า UIWindow ควรทำอย่างไรในกรณีนี้ openradar.appspot.com/42372793
steipete

73

คุณจะต้องทำการตรวจจับต่าง ๆ ของ iPhone X ขึ้นอยู่กับความต้องการที่แท้จริง

สำหรับจัดการกับรอยบนสุด (แถบสถานะ, แถบนำทาง) ฯลฯ

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
}

สำหรับจัดการกับตัวบ่งชี้ด้านล่าง (tabbar) เป็นต้น

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
}

สำหรับขนาดพื้นหลังคุณสมบัติเต็มหน้าจอ ฯลฯ

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

หมายเหตุ: ในที่สุดก็ผสมกับUIDevice.current.userInterfaceIdiom == .phone
หมายเหตุ: วิธีนี้ต้องมีกระดานเรื่องราว LaunchScreen หรือ LaunchImages ที่เหมาะสม

สำหรับอัตราส่วนพื้นหลังคุณสมบัติการเลื่อน ฯลฯ

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
}

หมายเหตุ: วิธีการนี้จะต้องมีสตอรี่บอร์ด LaunchScreen หรือ LaunchImages ที่เหมาะสม

สำหรับการวิเคราะห์สถิติการติดตาม ฯลฯ

รับตัวระบุเครื่องและเปรียบเทียบกับค่าเอกสาร:

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"
}

หากต้องการรวมเครื่องมือจำลองเป็น iPhone X ที่ถูกต้องในการวิเคราะห์ของคุณ:

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"
}

หากต้องการรวม iPhone XS, XS Max และ XR เพียงมองหารุ่นที่ขึ้นต้นด้วย "iPhone11":

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

สำหรับการสนับสนุน 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
}

ฉันหวังว่าreturn LAContext().biometryType == .typeFaceIDจะใช้งานได้แม้ว่าผู้ใช้จะปฏิเสธ canEvaluatePolicy แต่มันใช้งานไม่ได้สำหรับฉันมันยังคงส่งคืน.none
Jeremy

ด้วย @Jeremy เป็นพฤติกรรมที่บันทึกไว้ซึ่งเป็นผลมาจากนโยบายความเป็นส่วนตัวของ Apple นั่นเป็นเหตุผลที่ความคิดเห็นข้างต้นวิธีการ
Cœur

อ่าฉันตีความความคิดเห็นของคุณผิด ฉันคิดว่าคุณหมายถึงการใช้ canEvaluatePolicy อาจล้มเหลวดังนั้นควรใช้สิ่งต่อไปนี้แทน ฉันคิดว่ามันแปลก ๆ นิดหน่อยที่คุณได้รับอนุญาตให้ตรวจสอบว่าอุปกรณ์มี ID ประจำตัวจนกว่าผู้ใช้จะตอบกลับการสลับแล้วคุณจะไม่สามารถตรวจสอบได้อีกต่อไป ฉันควรจะให้ข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์เพื่อพูดว่าไปที่การตั้งค่าและสลับ Face ID ได้อย่างไร
Jeremy

@ Jeremy ฉันไม่ได้เป็นเจ้าของ iPhone X ดังนั้นฉันไม่รู้ บางทีคุณอาจใช้การตรวจจับโมเดลด้านบน ( model == "iPhone10,3" || model == "iPhone10,6") และหากcanUseFaceIDส่งคืนค่าเท็จแสดงว่าผู้ใช้นั้นถูกปฏิเสธ
Cœur

1
@MateoOlaya ไม่มีอะไรในคำตอบของฉันที่จะถูกปฏิเสธโดย Apple: คุณสามารถใช้มันได้ทั้งหมด
Cœur

42

คุณสามารถทำเช่นนี้เพื่อตรวจจับอุปกรณ์iPhone Xตามขนาด

รวดเร็ว

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

วัตถุประสงค์ - ค

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

ป้อนคำอธิบายรูปภาพที่นี่

แต่ ,

วิธีนี้ไม่เพียงพอ จะเกิดอะไรขึ้นถ้า Apple ประกาศ iPhone เครื่องถัดไปที่มีขนาดเท่ากันของ iPhone X ดังนั้นวิธีที่ดีที่สุดคือการใช้สตริงฮาร์ดแวร์เพื่อตรวจจับอุปกรณ์

สำหรับสตริงฮาร์ดแวร์อุปกรณ์ที่ใหม่กว่ามีดังนี้

iPhone 8 - iPhone10,1หรือiPhone 10,4

iPhone 8 Plus - iPhone10,2หรือiPhone 10,5

iPhone X - iPhone10,3หรือiPhone10,6


2
คุณควรใช้[UIDevice currentDevice]แทน[[UIDevice alloc] init]
S. Matsepura

ปัญหาเดียวกับสตริงฮาร์ดแวร์คือมันไม่ทำงานบนเครื่องจำลอง
กลัว

38

ตรวจสอบชื่อรุ่นของอุปกรณ์ / เครื่องไม่ได้ใช้จุด / พิกเซลนับในรหัสของคุณโดยตรงก็ยากรหัสและความหมายสำหรับฮาร์ดแวร์อุปกรณ์รุ่นอุปกรณ์ที่เป็นตัวบ่งชี้เฉพาะเพียงสำหรับประเภทของอุปกรณ์ที่จะแข่งขัน

#import <sys/utsname.h>

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

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

ผลลัพธ์:

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

อ้างถึงคำตอบนี้

การติดตั้งโค้ดแบบสมบูรณ์:

#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
คำตอบที่ยอดเยี่ยมเพราะมันจัดการกับการจำลองอย่างถูกต้อง โปรดเพิ่มบรรทัด #import ลงในส่วน "รหัสเต็ม" ฉันพลาด (คัดลอก / วาง) ในความพยายามครั้งแรกของฉัน
mpoisot

1
นั่นคือวิธีที่ฉันชอบ อ้างถึงวิกินี้สำหรับรายการสตริงอุปกรณ์รุ่นที่สมบูรณ์ ในฐานะที่เป็นความคิดเห็นด้านข้าง @ "iphone10,3" อาจถูกมองว่าเป็นรหัสที่ยาก
YvesLeBorg

1
@YvesLeBorg ใช่มันเป็นปัญหาที่ถกเถียงกันมาก ฉันคิดว่าสตริงโมเดลฮาร์ดแวร์มีตัวระบุเฉพาะนอกเหนือจากจุดหน้าจอของอุปกรณ์ โดยทั่วไปจะใช้สำหรับสถิติข้อมูล
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)

กำหนด IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] ขอบเขต] .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"]

หมายเหตุ: - ระวังใช้งานได้ดีสำหรับการวางแนวตั้งเท่านั้น


2
ระวังใช้งานได้ดีสำหรับการวางแนวตั้งเท่านั้น
CFIFok

1
ขอบคุณสำหรับสิ่งนี้. ทำได้ดี. ในโหมดแนวนอนคุณต้องปรับตัวเลขเหล่านั้น จำนวนมายากลของ iPhoneX ในโหมดแนวนอนคือ 375.0
pvella

มี iPhone Plus / Max / Pro สองสามเครื่องที่ใช้nativeScaleด้วย3.0ใช่ไหม
Itachi

24

หลังจากดูคำตอบทั้งหมดนี่คือสิ่งที่ฉันทำลงไป:

โซลูชัน (รองรับ 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
    }
}

ใช้

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

บันทึก

Pre Swift 4.1 คุณสามารถตรวจสอบว่าแอพกำลังทำงานอยู่บนตัวจำลองดังเช่น:

TARGET_OS_SIMULATOR != 0

ตั้งแต่ Swift 4.1 เป็นต้นไปคุณสามารถตรวจสอบว่าแอปทำงานบนเครื่องจำลองโดยใช้เงื่อนไขแพลตฟอร์มสภาพแวดล้อมเป้าหมาย :

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

(วิธีที่เก่ากว่าจะยังคงใช้งานได้ แต่วิธีการใหม่นี้เป็นหลักฐานเพิ่มเติมในอนาคต)


แอปเปิ้ลจะดีกับนี้หรือไม่?
Rajj Surjeet

@ commando24 ใช่ฉันไม่เห็นเหตุผลที่พวกเขาจะปฏิเสธแอปเพราะรหัสนี้
Cloud9999Strife

18

คำตอบทั้งหมดเหล่านี้ซึ่งยึดตามมิติข้อมูลมีความอ่อนไหวต่อพฤติกรรมที่ไม่ถูกต้องในอุปกรณ์ในอนาคต พวกเขาจะทำงานวันนี้ แต่ถ้ามี iPhone ปีหน้าขนาดเท่ากัน แต่มีกล้อง ฯลฯ อยู่ใต้กระจกดังนั้นจึงไม่มี "รอยบาก" หากตัวเลือกเดียวคืออัปเดตแอปแสดงว่าเป็นโซลูชันที่ไม่ดีสำหรับคุณและลูกค้าของคุณ

คุณสามารถตรวจสอบสตริงโมเดลฮาร์ดแวร์เช่น "iPhone10,1" ได้ แต่เป็นปัญหาเพราะบางครั้ง Apple ปล่อยหมายเลขรุ่นที่แตกต่างกันสำหรับผู้ให้บริการที่แตกต่างกันทั่วโลก

วิธีที่ถูกต้องคือการออกแบบเลย์เอาต์ด้านบนหรือแก้ไขปัญหาที่คุณมีกับความสูงของแถบการนำทางที่กำหนดเอง (นั่นคือสิ่งที่ฉันต้องการจะเน้น) แต่ถ้าคุณตัดสินใจที่จะไม่ทำสิ่งใดสิ่งหนึ่งเหล่านั้นจงตระหนักว่าสิ่งที่คุณกำลังทำอยู่คือการแฮ็คเพื่อให้เรื่องนี้ทำงานได้ในวันนี้และคุณจะต้องแก้ไขให้ถูกจุดในบางครั้งอาจจะหลายครั้ง การทำงาน


1
ขวา. การปรับสมมติฐานที่ว่าหมายเลข X จะเป็น A เสมอไปที่หมายเลข X จะเป็น A เสมอยกเว้นว่าเงื่อนไข Y เมื่อมันเป็น B นั้นจะขุดลึกลงไป ขนาดขึ้นอยู่กับพื้นที่ปลอดภัยที่ได้รับการเสนอชื่อโดย Apple ไม่ใช่เป็นการเดาที่สอง
Tommy

2
ฉันจะกังวลเกี่ยวกับ iPhone ต่อไปเมื่อมันออกมาจริง ฉันต้องการให้แอปของฉันทำงานวันนี้
Vahid Amiri

13

SWIFT 4+ คำตอบ

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

หมายเหตุ: ต้องการอุปกรณ์จริงสำหรับการทดสอบ

การอ้างอิง

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

สำหรับวิธีที่ 1 คุณสามารถนำคุณสมบัติ "หน้าต่าง var" ออกไปนอก func และเพียงแค่ "ปล่อย" คงอยู่ภายใน (พิมพ์ UIWindow นั่นคือไม่จำเป็น) ฉันชอบคำตอบนี้ตั้งแต่เริ่มต้น self.view.window อาจเป็นศูนย์และ UIApplication.shared.keyWindow อาจเป็นศูนย์ในขณะที่สร้าง UIWindow ด้วยวิธีนี้ใช้ได้ทุกครั้ง
Rolleric

11

SWIFT 4/5 ส่วนขยายที่สามารถใช้ซ้ำได้พร้อมการรองรับ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
ส่วนขยายที่ดี แต่มีประโยชน์มากที่สุดที่นี่คือUIDevice.current.hasHomeButton
WINSergey

1
@ale_stro มันเป็นการดีที่จะใช้ userInterfaceIdiom ในการกำหนดอุปกรณ์สำหรับแอพสากลหรือไม่ คนส่วนใหญ่ไม่แนะนำสิ่งนี้ มีอันตรายใด ๆ ที่จะใช้หรือไม่
shaqir saiyed

10

ใช่มันเป็นไปได้ ดาวน์โหลดส่วนขยาย UIDevice-Hardware (หรือติดตั้งผ่าน CocoaPod 'UIDevice-Hardware') จากนั้นใช้:

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

โปรดทราบว่าสิ่งนี้จะไม่ทำงานใน Simulator เท่านั้นบนอุปกรณ์จริง


รหัสอุปกรณ์ทั้งหมดที่นี่: iphonesoft.fr/2016/10/31/… ตัวอย่าง: iPhone X: iPhone10,5 และ iPhone10,6
Medhi

สตริงฮาร์ดแวร์จากวิกิพีเดียกล่าวว่า "iPhone10,3 และ iPhone10,6" @Medhi
Itachi

@Medhi คุณสามารถใช้ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]ใน Simulator เพื่อรับค่าจริงจาก Xcode
Cœur

9

ตามการตอบสนองของ @ saswanb นี่เป็นรุ่น Swift 4:

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

แถบสถานะจะถูกพิจารณานอกพื้นที่ปลอดภัยด้วย! ดังนั้นสิ่งนี้จะคืนค่าบวกเท็จ! มันควรจะสูงกว่า 20 จุด (ความสูงของแถบสถานะ) สิ่งนี้จะส่งกลับเช่นกันหากอุปกรณ์เป็น iPhone Xs, R หรือ Xs Max
MQoder

รหัสใช้งานได้ดี แต่ต้องระวัง: keyWindowเป็นnilจนควบคุมมุมมองหลักได้เรียกviewDidAppear
เคซี่ย์

9

ฉันรู้ว่ามันเป็นเพียงโซลูชันของSwiftแต่สามารถช่วยเหลือใครบางคนได้

ฉันมีglobals.swiftในทุกโครงการและสิ่งหนึ่งที่ฉันเพิ่มอยู่เสมอคือDeviceTypeการตรวจสอบอุปกรณ์ของผู้ใช้อย่างง่ายดาย:

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

จากนั้นให้ใช้:

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

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

หากคุณใช้LaunchImageในโครงการของคุณตรวจสอบให้แน่ใจว่าได้เพิ่มรูปภาพสำหรับอุปกรณ์ที่รองรับทั้งหมด (เช่น XS Max, XR) เพราะUIScreen.main.boundsจะไม่ส่งคืนค่าที่เหมาะสมหากไม่มี


1
เพื่อนใหม่จาก Swift ถามว่าจะใช้สิ่งนี้อย่างไรในกรณีที่คนอื่นไม่รู้… if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling

5

คำตอบทั้งหมดที่ใช้heightคือเพียงครึ่งเดียวของเรื่องราวด้วยเหตุผลหนึ่งข้อ หากคุณกำลังจะตรวจสอบเช่นนั้นเมื่อการวางแนวอุปกรณ์landscapeLeftหรือlandscapeRightการตรวจสอบจะล้มเหลวเพราะheightมันสลับกับwidthจะสลับออกมาพร้อมกับ

นั่นเป็นสาเหตุที่โซลูชันของฉันมีลักษณะเช่นนี้ใน 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
    }
}

เพียงใช้ nativeBounds แทน
Leo Dabus

4

คุณไม่ควรสันนิษฐานว่าอุปกรณ์เดียวที่ Apple เปิดตัวด้วยความสูง UINavigationBar ที่แตกต่างกันจะเป็น iPhone X ลองแก้ไขปัญหานี้โดยใช้วิธีแก้ปัญหาทั่วไปเพิ่มเติม หากคุณต้องการให้บาร์มีขนาดใหญ่กว่าความสูงเริ่มต้น 20px โค้ดของคุณควรเพิ่ม 20px ไปที่ความสูงของแถบแทนที่จะตั้งเป็น 64px (44px + 20px)


ดังนั้นวิธีการแก้ปัญหาอื่นที่คุณต้องเสนอคืออะไร?
Stephane Mathis

@xaphod มีคำตอบที่ดีกว่าตอนนี้
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

สวิฟท์ 3 + 4:

โดยไม่จำเป็นต้องมีค่าพิกเซลขนาดอุปกรณ์ใด ๆ

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

ตัวอย่าง:

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
มันจะให้คุณ 812 ถ้าคุณอัปโหลดภาพเริ่มต้นสำหรับ iPhone X จนกว่าฉันจะคิดว่ามันจะส่งคืนคุณ iPhone 7 ขนาดไม่แน่ใจว่า ...
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
คำตอบที่ดีที่สุด! โดยไม่จำเป็นต้องมีค่าพิกเซลขนาดอุปกรณ์ใด ๆ
Peter Kreinz

3

โดยทั่วไปแล้วโปรแกรมเมอร์จำเป็นต้องใช้เพื่อ จำกัด ให้อยู่ด้านบนหรือด้านล่างดังนั้นวิธีการเหล่านี้จึงสามารถช่วยได้

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
}

สำหรับก่อน iPhone X วิธีการเหล่านี้จะคืนค่า: 0

สำหรับ iPhone X: 44 และ 34 ตามลำดับ

จากนั้นเพียงเพิ่มความพิเศษเหล่านี้ลงในข้อ จำกัด ด้านบนหรือด้านล่าง


3

สำหรับผู้ที่ได้รับ 2001px แทนที่จะเป็น 2436px สำหรับความสูงแบบเนทีฟ (เช่นฉัน) นั่นเป็นเพราะคุณสร้างแอปของคุณด้วย SDK ที่เก่ากว่าก่อนหน้า iOS 11 (Xcode 8 แทนที่จะเป็น Xcode 9) ด้วย SDK รุ่นเก่า iOS จะแสดงแอพ "กล่องดำ" ใน iPhone X แทนที่จะขยายขอบหน้าจอไปจนถึงขอบด้านบน "เซ็นเซอร์บาก" สิ่งนี้จะลดขนาดหน้าจอซึ่งเป็นสาเหตุที่คุณสมบัตินั้นส่งคืน 2001 แทน 2436

ทางออกที่ง่ายที่สุดคือการตรวจสอบทั้งสองขนาดถ้าคุณสนใจในการตรวจจับอุปกรณ์เท่านั้น ฉันใช้วิธีนี้ในการตรวจจับ FaceID ในขณะที่สร้างด้วย Xcode SDK ที่เก่ากว่าซึ่งไม่มีค่า ENUM ที่ระบุประเภทไบโอเมตริกซ์ ในสถานการณ์เช่นนี้การตรวจจับอุปกรณ์โดยใช้ความสูงของหน้าจอดูเหมือนจะเป็นวิธีที่ดีที่สุดในการทราบว่าอุปกรณ์นั้นมี FaceID เทียบกับ TouchID โดยไม่ต้องอัปเดต Xcode หรือไม่


3

อย่าใช้ขนาดพิกเซลของหน้าจอตามที่โซลูชันอื่น ๆ แนะนำไว้นี่เป็นสิ่งที่แย่เพราะอาจส่งผลให้เกิดผลบวกปลอมสำหรับอุปกรณ์ในอนาคต จะไม่ทำงานหาก UIWindow ยังไม่ได้แสดงผล (AppDelegate) จะไม่ทำงานในแอพแนวนอนและอาจล้มเหลวในการจำลองหากตั้งค่ามาตราส่วน

ฉันทำมาโครเพื่อจุดประสงค์นี้แทนแล้วมันใช้งานง่ายมากและอาศัยแฟล็กฮาร์ดแวร์เพื่อป้องกันปัญหาดังกล่าวข้างต้น

แก้ไข: อัปเดตเพื่อรองรับ iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Max


ใช้:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

ใช่จริง ๆ


มาโคร

เพียงคัดลอกวางที่ใดก็ได้ฉันชอบที่ด้านล่างสุดของไฟล์. h หลังจากนั้น @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]);\
})()\
)

เหตุผลเดียวที่ฉันคิดได้ในการตรวจจับ iPhoneX คือการหลีกเลี่ยงรอยบากที่ด้านบนของหน้าจอ ถ้าเป็นเช่นนั้นคุณสามารถตรวจสอบ safeArea.top เพื่อตรวจสอบขนาดของรอยกล่าว เพียงให้แน่ใจว่าคุณวัดหลังจาก UIWindow ได้โหลดแล้วไม่ใช่ในระหว่าง viewDidLoad แต่หนึ่งเธรดรอบหลังจาก:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw

2

ฉันอธิบายรายละเอียดของคำตอบของผู้อื่นและทำการขยายอย่างรวดเร็วใน UIDevice ฉันชอบความรวดเร็วและ "ทุกอย่างตามลำดับ" & ทำให้เป็นละออง ฉันได้สร้างโซลูชันที่ใช้งานได้ทั้งบนอุปกรณ์และเครื่องมือจำลอง

ข้อดี: - ส่วนต่อประสานที่เรียบง่ายการใช้งานเช่นUIDevice.current.isIPhoneX - UIDeviceModelTypeenum ให้ความสามารถในการขยายคุณสมบัติและรูปแบบเฉพาะที่คุณต้องการใช้ในแอพของคุณเช่น cornerRadius

ข้อเสีย: - เป็นโซลูชันเฉพาะรุ่นไม่เฉพาะเจาะจงความละเอียด - เช่นถ้า Apple จะผลิตรุ่นอื่นที่มีสเปคเดียวกันสิ่งนี้จะทำงานไม่ถูกต้องและคุณต้องเพิ่มรุ่นอื่นเพื่อที่จะทำให้งานนี้ => คุณต้องอัปเดต แอป

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

แทนที่จะใช้Mirrorมันจะเร็วกว่าที่จะใช้sysctlbynameเช่นเดียวกับที่ทำใน Cloud9999Strife คำตอบ (และในคำตอบของฉันด้วย)
Cœur

2

ฉันใช้ความสูงของ Status Bar Frame เพื่อตรวจสอบว่าเป็น iPhone X หรือไม่:

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

นี่เป็นแอปพลิเคชั่นสำหรับถ่ายภาพบุคคล คุณสามารถตรวจสอบขนาดตามการวางแนวของอุปกรณ์ นอกจากนี้บน iPhone อื่น ๆ แถบสถานะอาจถูกซ่อนดังนั้นความสูงของเฟรมจึงเป็น0เช่นนั้น บน iPhone X แถบสถานะจะไม่ถูกซ่อน


คุณสามารถซ่อนสถานะ iPhoneX ของบาร์controllerโดยใช้สิ่งนี้: - (BOOL)prefersStatusBarHidden { return YES; } จากนั้นความสูงของแถบสถานะคือ 0.
无夜之星辰

@ 无夜之星辰ฉันตรวจสอบเรื่องนี้ในเวลาบูตใน AppDelegate
Tiois

2

ฉันใช้รหัสของ Peter Kreinz (เพราะมันสะอาดและทำตามที่ฉันต้องการ) แต่แล้วฉันก็รู้ว่ามันทำงานได้ก็ต่อเมื่ออุปกรณ์อยู่ในแนวตั้ง (เนื่องจากช่องว่างด้านบนจะอยู่ด้านบนอย่างชัดเจน) ดังนั้นฉันจึงสร้างส่วนขยาย การหมุนด้วยการหมุนตามลำดับโดยไม่มีการถ่ายทอดขนาดหน้าจอ:

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

และในไซต์โทรของคุณคุณเพียง:

let res = UIDevice.current.isIphoneX

2

หรือคุณสามารถดูพ็อด ' DeviceKit ' เมื่อติดตั้งแล้วสิ่งที่คุณต้องทำเพื่อตรวจสอบอุปกรณ์คือ:

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

2

พ.ย. 2562:

นี่คือสิ่งที่ฉันใช้ในโครงการผลิตทั้งหมดของฉัน โปรดทราบว่าส่วนสำคัญนี้ค่อนข้างยาว

  1. สิ่งนี้ไม่ได้ใช้การคำนวณความกว้างหรือความสูง แต่เป็นการคำนวณ:
  2. มันตรวจสอบรูปแบบสตริงอุปกรณ์
  3. ไม่มีความเสี่ยงในการทำให้งานสร้างของคุณถูกปฏิเสธโดย Apple เนื่องจากการใช้ API ส่วนตัว / ที่ไม่มีเอกสารใด ๆ
  4. ทำงานร่วมกับเครื่องจำลอง💯

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

การใช้งาน: ให้สิ่งที่ใส่เข้าไป: CGFloat = DeviceUtility.isIphoneXType? 50.0: 40.0


ทำงานได้อย่างสมบูรณ์แบบ ขอบคุณ ฉันใช้มันในโครงการ SwiftUI
LondonGuy

1

ฉันต้องแก้ไขปัญหาเดียวกันเมื่อเร็ว ๆ นี้ และในขณะที่คำถามนี้ได้รับคำตอบอย่างชัดเจน ("ไม่") สิ่งนี้อาจช่วยผู้อื่นที่ต้องการพฤติกรรมการจัดวางเฉพาะ iPhone X

ฉันไม่สนใจว่าอุปกรณ์นั้นเป็น iPhone X หรือไม่ฉันสนใจว่าอุปกรณ์นั้นมีหน้าจอที่มีรอยบากหรือไม่

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
}

คุณสามารถเขียน hasOnScreenHomeIndicatorตัวแปรตามบรรทัดเดียวกัน (แม้ว่าตรวจสอบพื้นที่ปลอดภัยด้านล่างหรืออาจ?)

ด้านบนใช้ส่วนขยายของฉันUIViewเพื่อความสะดวกในการเข้าถึงส่วนที่ปลอดภัยใน iOS 10 และรุ่นก่อนหน้า

@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

ในแนวตั้งเท่านั้นฉันใช้ความกว้างและความสูงของกรอบมุมมองเพื่อตรวจสอบ:

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")
    }

}

ขนาดหน้าจอแนวตั้งแสดงไว้ที่นี่

ป้อนคำอธิบายรูปภาพที่นี่


0

มีสาเหตุหลายประการที่ต้องการทราบว่าอุปกรณ์คืออะไร

  1. คุณสามารถตรวจสอบความสูงของอุปกรณ์ (และความกว้าง) สิ่งนี้มีประโยชน์สำหรับโครงร่าง แต่โดยปกติคุณไม่ต้องการทำเช่นนั้นหากคุณต้องการทราบอุปกรณ์ที่แน่นอน

  2. UIView.safeAreaInsetsเพื่อวัตถุประสงค์ในรูปแบบนี้คุณยังสามารถใช้

  3. หากคุณต้องการแสดงชื่ออุปกรณ์ตัวอย่างเช่นเพื่อรวมไว้ในอีเมลเพื่อวัตถุประสงค์ในการวินิจฉัยหลังจากดึงข้อมูลรุ่นอุปกรณ์โดยใช้sysctl ()คุณสามารถใช้ชื่อเทียบเท่านี้เพื่อกำหนดชื่อ:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.