ios - tab - swift navigation bar height




檢測設備是否為iPhone X. (20)

我的iOS應用程序使用 UINavigationBar 的自定義高度,這會導致新的iPhone X出現一些問題。

如果應用程序在iPhone X上運行,是否有人已經知道如何以編程方式(在Objective-C中)進行 可靠 檢測?

編輯:

當然可以檢查屏幕的大小,但是,我想知道是否有像 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功能)或者我是否必須使用間接測量”

到目前為止給出的答案,我認為答案是“不,沒有直接的方法。測量是要走的路”。


不要像其他解決方案所建議的那樣使用屏幕像素大小,這很糟糕,因為它可能導致未來設備出現誤報; 如果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]);\
})()\
)

對於那些獲得2001px而不是2436px的本地邊界高度(像我一樣),這是因為你在iOS 11(Xcode 8而不是Xcode 9)之前使用較舊的SDK構建了你的應用程序。 使用較舊的SDK,iOS將在iPhone X上顯示應用程序“黑盒子”,而不是將屏幕邊緣到邊緣延伸,超出頂部的“傳感器缺口”。 這會減小屏幕尺寸,這就是該屬性返回2001而不是2436的原因。

最簡單的解決方案是,如果您只對設備檢測感興趣,只需檢查兩種尺寸。 我使用這種方法檢測FaceID,同時使用較舊的Xcode SDK構建,該SDK沒有指定生物識別類型的ENUM值。 在這種情況下,使用屏幕高度的設備檢測似乎是了解設備是否具有FaceID與TouchID而不必更新Xcode的最佳方式。


我最近不得不解決同樣的問題。 雖然這個問題得到了明確的回答(“否”),但這可能有助於其他需要iPhone X特定佈局行為的人。

我對該設備是否是iPhone X並不感興趣。我對該設備是否有缺口顯示感興趣。

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

你也可以 hasOnScreenHomeIndicator 沿同一行 寫一個 變量(雖然檢查底部的安全區域,也許?)。

以上使用我的擴展 UIView ,方便訪問iOS 10及更早版本的安全區域insets。

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

我正在使用 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

或者,您可以查看“ DeviceKit ”窗格。 安裝完成後,您需要做的就是檢查設備:

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
}

有幾個原因想知道設備是什麼。

  1. 您可以檢查設備高度(和寬度)。 這對佈局很有用,但如果您想知道確切的設備,通常不希望這樣做。

  2. 出於佈局目的,您也可以使用 UIView.safeAreaInsets

  3. 如果要顯示設備名稱(例如,要包含在用於診斷目的的電子郵件中),則在使用後檢索設備模型後 sysctl () ,可以使用等效的名稱來表示名稱:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X

通常,程序員需要將其約束到頂部或底部,因此這些方法可以提供幫助

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相應

然後只需將這些額外內容添加到頂部或底部約束


你可以這樣做,根據尺寸檢測 iPhone X 設備。

迅速

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

目標 - C.

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


在查看了所有答案後,這就是我最終做的事情:

解決方案(兼容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開始,您可以使用 Target環境平台條件 檢查應用程序是否在模擬器上運行:

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

(舊的方法仍然有效,但這種新方法更具前瞻性)


對的,這是可能的。 下載 UIDevice-Hardware擴展 (或通過CocoaPod'UIDevice-Hardware'安裝),然後使用:

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

請注意,這在模擬器中不起作用,僅在實際設備上有效。


您應根據實際需要對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
}

我知道這只是一個 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.maxLength == 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 將不會返回正確的值。


根據@ saswanb的回复,這是一個Swift 4版本:

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

根據你的問題,答案是否定的。 沒有直接的方法。 有關更多信息,您可以在此處獲取信息:

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

        case 2688:
            print("iPhone XS Max")

        case 1792:
            print("iPhone XR")

        default:
            print("Unknown")
        }
    }

Objective-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:
            printf("iPhone X, XS");
            break;

        case 2688:
            printf("iPhone XS Max");
            break;

        case 1792:
            printf("iPhone XR");
            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");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2688) {
        Console.WriteLine("iPhone XS Max");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1792) {
        Console.WriteLine("iPhone XR");
    } else {
        Console.WriteLine("Unknown");
    }
}

根據您的問題如下:

或者使用 screenSize.height 作為float 812.0f 而不是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");
    }

有關更多信息,請參閱iOS人機界面指南中的以下頁面:

斯威夫特

使用 topNotch 檢測:

var hasTopNotch: Bool {
    if #available(iOS 11.0,  *) {
        return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
    }

    return false
}

Objective-C

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

    return  NO;
}

更新

請勿使用 userInterfaceIdiom 屬性來標識設備類型,因為 userInterfaceIdiom 文檔 說明:

對於通用應用程序,您可以使用此屬性來定制應用程序對特定類型設備的行為。 例如,iPhone和iPad設備具有不同的屏幕尺寸,因此您可能希望根據當前設備的類型創建不同的視圖和控件。

也就是說,此屬性僅用於標識正在運行的應用程序的視圖樣式。 但是,iPhone應用程序(不是通用)可以通過App store安裝在iPad設備中,在這種情況下, userInterfaceIdiom 也會返回 UIUserInterfaceIdiomPhone

正確的方法是通過 uname 獲取機器名稱。 請查看以下詳細信息:


由於一個原因,所有使用 height 的答案只是故事的一半。 如果您要在設備方向為 landscapeLeftlandscapeRight 進行檢查,則檢查將失敗,因為 heightwidth 交換。

這就是為什麼我的解決方案在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
    }
}

肖像中, 我使用視圖框架的寬度和高度來檢查:

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

}

此處列出了縱向屏幕尺寸


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

SWIFT 4+答案

iPhone X,XR,XS,XSMAX:

注意:需要真正的設備進行測試

Reference

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

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

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
}




iphone-x