我的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功能),还是我必须使用间接测量?”

根据目前给出的答案,我假设答案是“不,没有直接的方法。测量是要走的路。”


当前回答

在看完所有的答案后,我最后是这么做的:

解决方案(兼容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
    }
}

Use

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

Note

Pre Swift 4.1你可以检查应用程序是否在模拟器上运行,就像这样:

TARGET_OS_SIMULATOR != 0

从Swift 4.1开始,你可以使用目标环境平台条件检查应用程序是否在模拟器上运行:

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

(旧的方法仍然有效,但这个新方法更经得起未来的考验)

其他回答

根据@saswanb的回复,这是Swift 4的版本:

var iphoneX = false
if #available(iOS 11.0, *) {
    if ((UIApplication.shared.keyWindow?.safeAreaInsets.top)! > CGFloat(0.0)) {
        iphoneX = true
    }
}
- (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;
    }
}

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

我试图使工作之前的答复,他们都没有为我工作。所以我为SwiftUI找到了一个解决方案。创建名为UIDevice+Notch.swift的文件

其内容:

extension UIDevice {
    var hasNotch: Bool {
        let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
        return bottom > 0
    }
}

用法:

if UIDevice.current.hasNotch {
    //... consider notch 
} else {
    //... don't have to consider notch 
}

所有这些基于尺寸的答案都很容易在未来的设备上出现错误行为。它们今天还能用,但如果明年有一款同样大小的iPhone,但在玻璃下面有摄像头等,所以没有“缺口”呢?如果唯一的选择是更新应用程序,那么这对你和你的客户来说都是一个糟糕的解决方案。

您还可以检查硬件型号字符串,如“iPhone10,1”,但这是有问题的,因为有时苹果会为世界各地的不同运营商发布不同的型号。

正确的方法是重新设计顶部布局,或者解决自定义导航栏高度的问题(这是我关注的重点)。但是,如果你决定不做这两件事中的任何一件,你要意识到无论你现在做什么都是为了让它工作,你需要在某个时候纠正它,也许是多次,以保持它工作。