7

In my application, I was implemented pull-to-refresh feature and custom loading icon. In IPhone which has dynamic island, It was overlapsed my loading icon.

I want to detect device which has dynamic island or not. If it has, I will add some top space to it.

Tar journey
  • 379
  • 4
  • 13

3 Answers3

7
  1. According to the live activity documentation, we can only detect whether the device supports Live activity, but we don't know if the device has dynamic island

  2. I use the window safeAreaInsets value to detect dynamic island. when the device orientation is portrait, safeAreaInsets.top is equal to 59(Display Zoom Default), or 51(Display Zoom Large Text).

  3. This is likely to support the iPhone15 Pro/iPhone15 Pro Max and later models.

usage: print(UIDevice.current.hasDynamicIsland)

extension UIDevice {
    
    // Get this value after sceneDidBecomeActive
    var hasDynamicIsland: Bool {
        // 1. dynamicIsland only support iPhone
        guard userInterfaceIdiom == .phone else {
            return false
        }
               
        // 2. Get key window, working after sceneDidBecomeActive
        guard let window = (UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.flatMap { $0.windows }.first { $0.isKeyWindow}) else {
            print("Do not found key window")
            return false
        }
       
        // 3.It works properly when the device orientation is portrait
        return window.safeAreaInsets.top >= 51
    }
}
wlixcc
  • 1,132
  • 10
  • 14
  • 2
    I tested this with multiple different devices to make sure there is no conflict with other sizes and they all worked, this is a more future-proof solution for now. – shadow of arman Mar 14 '23 at 08:37
4

Currently, as far as I know, dynamic island is will included in ActivityKit on late of 2022. You can check from this link for ActivityKit and Apple's thread about it. And Apple doesn't provide way to check dynamic island is on device or not.

But there is a workaround for you to get the thing you want. Currently dynamic island only available on iPhone 14 Pro and iPhone 14 Pro Max. So just need to check this both device.

Update: Thanks to this link for type model, name model type of iPhone 14 Pro and iPhone 14 Pro Max is iPhone15,2 and iPhone15,3 so we just need to check these case.

Code will be like this

extension UIDevice {
    func checkIfHasDynamicIsland() -> Bool {
        if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
            let nameSimulator = simulatorModelIdentifier
            return nameSimulator == "iPhone15,2" || nameSimulator == "iPhone15,3" ? true : false
        }
        
        var sysinfo = utsname()
        uname(&sysinfo) // ignore return value
        let name =  String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters)
        return name == "iPhone15,2" || name == "iPhone15,3" ? true : false
    }
}

Usage

let value = UIDevice().checkIfHasDynamicIsland()
print("value: ", value)
Thang Phi
  • 1,641
  • 2
  • 7
  • 16
  • According to Apple documentation, the name is a generic device name on iOS 16: "iPhone", and the user's assigned name in older versions. See https://developer.apple.com/documentation/uikit/uidevice/1620015-name?language=objc Neither will be useful for this purpose. For how to really find the iPhone model, see https://stackoverflow.com/a/26962452/908621 – fishinear Oct 08 '22 at 13:45
  • This does not support iPhone 15 & later versions... – Ahmadreza Jan 29 '23 at 10:51
  • 2
    Dear @Ahmadreza, when will iphone 15 release or there is another way to achieve I will update the answer – Thang Phi Jan 31 '23 at 00:55
0

I used the Device framework and implemented an additional computed property in extension to detect the dynamic island availability.

extension Device {
    static var hasDynamicIsland: Bool {
        switch version() {
        case .iPhone14Pro, .iPhone14Pro_Max:
            return true
        case .iPhone2G:
            return false
        case .iPhone3G:
            return false
        case .iPhone3GS:
            return false
        case .iPhone4:
            return false
        case .iPhone4S:
            return false
        case .iPhone5:
            return false
        case .iPhone5C:
            return false
        case .iPhone5S:
            return false
        case .iPhone6:
            return false
        case .iPhone6Plus:
            return false
        case .iPhone6S:
            return false
        case .iPhone6SPlus:
            return false
        case .iPhoneSE:
            return false
        case .iPhone7:
            return false
        case .iPhone7Plus:
            return false
        case .iPhone8:
            return false
        case .iPhone8Plus:
            return false
        case .iPhoneX:
            return false
        case .iPhoneXS:
            return false
        case .iPhoneXS_Max:
            return false
        case .iPhoneXR:
            return false
        case .iPhone11:
            return false
        case .iPhone11Pro:
            return false
        case .iPhone11Pro_Max:
            return false
        case .iPhoneSE2:
            return false
        case .iPhone12Mini:
            return false
        case .iPhone12:
            return false
        case .iPhone12Pro:
            return false
        case .iPhone12Pro_Max:
            return false
        case .iPhone13Mini:
            return false
        case .iPhone13:
            return false
        case .iPhone13Pro:
            return false
        case .iPhone13Pro_Max:
            return false
        case .iPhone14:
            return false
        case .iPhone14Plus:
            return false
        case .iPad1:
            return false
        case .iPad2:
            return false
        case .iPad3:
            return false
        case .iPad4:
            return false
        case .iPad5:
            return false
        case .iPad6:
            return false
        case .iPad7:
            return false
        case .iPad8:
            return false
        case .iPad9:
            return false
        case .iPadAir:
            return false
        case .iPadAir2:
            return false
        case .iPadAir3:
            return false
        case .iPadAir4:
            return false
        case .iPadMini:
            return false
        case .iPadMini2:
            return false
        case .iPadMini3:
            return false
        case .iPadMini4:
            return false
        case .iPadMini5:
            return false
        case .iPadMini6:
            return false
        case .iPadPro9_7Inch:
            return false
        case .iPadPro12_9Inch:
            return false
        case .iPadPro10_5Inch:
            return false
        case .iPadPro12_9Inch2:
            return false
        case .iPadPro11_0Inch:
            return false
        case .iPadPro12_9Inch3:
            return false
        case .iPadPro11_0Inch2:
            return false
        case .iPadPro11_0Inch3:
            return false
        case .iPadPro12_9Inch4:
            return false
        case .iPadPro12_9Inch5:
            return false
        case .iPodTouch1Gen:
            return false
        case .iPodTouch2Gen:
            return false
        case .iPodTouch3Gen:
            return false
        case .iPodTouch4Gen:
            return false
        case .iPodTouch5Gen:
            return false
        case .iPodTouch6Gen:
            return false
        case .iPodTouch7Gen:
            return false
        case .simulator:
            return false
        case .unknown:
            return false
        }
    }
}
Alexander Khitev
  • 6,417
  • 13
  • 59
  • 115