определить, является ли устройство iphone x

<p>в моем ios-приложении для <code>uinavigationbar</code> используется настраиваемая высота, что приводит к некоторым проблемам на новом iphone x. </p><p>кто-нибудь уже знает, как<strong> надежно </strong>обнаружить программно (в objective-c), если приложение работает на iphone x?</p><p><strong> правка: </strong></p><p>конечно, проверка размера экрана возможна, однако мне интересно, есть ли какой-нибудь метод "build in", такой как <code>target\_os\_iphone</code>, для обнаружения ios ...</p><pre><code>if (ui\_user\_interface\_idiom() == uiuserinterfaceidiomphone) { cgsize screensize = [[uiscreen mainscreen] bounds].size; if (screensize.height == 812) nslog(@"iphone x"); } </code></pre><p><strong> правка 2: </strong></p><p>я не думаю, что мой вопрос является дубликатом связанного вопроса. конечно, существуют методы для «измерения» различных свойств текущего устройства и использования результатов для определения того, какое устройство используется. тем не менее, это был не фактический вопрос моего вопроса, который я попытался подчеркнуть в своем первом редактировании. </p><p>фактический вопрос:<strong> «можно ли напрямую определить, является ли текущее устройство iphone x (например, с помощью некоторой функции sdk), или мне нужно использовать косвенные измерения» </strong>?</p><p>исходя из приведенных ответов, я предполагаю, что ответ «нет, прямых методов нет. измерения - это путь». </p>

<p>на основании вашего вопроса ответ - нет. прямых методов нет. для получения дополнительной информации вы можете получить информацию здесь:</p><ul> <li> <a href="https://stackoverflow.com/questions/11197509/how-to-get-device-make-and-model-on-ios/11197770#11197770">как получить марку и модель устройства на ios?</a> </li> </ul><p>а также</p><ul> <li> <a href="https://stackoverflow.com/questions/27775779/how-to-check-screen-size-of-iphone-4-and-iphone-5-programmatically-in-swift">как проверить размер экрана iphone 4 и iphone 5 программно в swift</a> </li> </ul><p>высота iphone x составляет 2436 пикселей</p><p>от <a href="https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions">размеры и разрешения экрана устройства</a> :</p><p> <a href="https://i.stack.imgur.com/jccjp.png"> <img src="https://i.stack.imgur.com/jccjp.png" alt="enter image description here"> </a> </p><p>от <a href="https://kapeli.com/cheat\_sheets/ios\_design.docset/contents/resources/documents/index">размеры и ориентация экрана устройства</a> :</p><p> <a href="https://i.stack.imgur.com/d0x04.png"> <img src="https://i.stack.imgur.com/d0x04.png" alt="enter image description here"> </a> </p><p><strong>swift 3 и более поздние версии</strong>:</p><pre><code>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") } } </code></pre><p><strong>objective-c</strong>:</p><pre><code>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; } } </code></pre><p><strong>xamarin.ios</strong>:</p><pre><code>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"); } } </code></pre><p>на основании вашего вопроса следующим образом:</p><p>или используйте <code>screensize.height</code> как float <code>812.0f</code>, а не int <code>812</code>.</p><pre><code>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"); } </code></pre><p>для получения дополнительной информации вы можете обратиться к следующей странице в руководстве по интерфейсу пользователя ios:</p><ul> <li> <a href="https://developer.apple.com/ios/human-interface-guidelines/visual-design/adaptivity-and-layout/">адаптивность и макет - визуальный дизайн - ios - руководство по интерфейсу пользователя</a> </li> </ul><p><strong>swift</strong>:</p><p>определить с помощью <code>topnotch</code>:</p><pre><code>var hastopnotch: bool { if #available(ios 11.0, *) { return uiapplication.shared.delegate?.window??.safeareainsets.top ?? 0 > 20 } return false } </code></pre><p><strong>objective-c</strong>:</p><pre><code>- (bool)hastopnotch { if (@available(ios 11.0, *)) { return [[[uiapplication sharedapplication] delegate] window].safeareainsets.top > 20.0; } return no; } </code></pre><p><strong>обновление</strong>:</p><p>не используйте свойство <code>userinterfaceidiom</code> для определения типа устройства, поскольку <a href="https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom">документация для userinterfaceidiom</a> объясняет:</p><blockquote> <p>для универсальных приложений вы можете использовать это свойство, чтобы адаптировать поведение вашего приложения для определенного типа устройства. например, устройства iphone и ipad имеют разные размеры экрана, поэтому вам может потребоваться создать разные представления и элементы управления в зависимости от типа текущего устройства.</p> </blockquote><p>то есть это свойство просто используется для определения стиля представления запущенного приложения. однако приложение iphone (не универсальное) может быть установлено на устройстве ipad через app store, в этом случае <code>userinterfaceidiom</code> также вернет <code>uiuserinterfaceidiomphone</code>.</p><p>правильный способ - получить имя машины через <code>uname</code>. проверьте следующее для деталей:</p><ul> <li> <a href="https://stackoverflow.com/questions/11197509/how-to-get-device-make-and-model-on-ios/11197770#11197770">как получить марку и модель устройства на ios?</a> </li> </ul>

<p>еще одна возможность, которая работает на ios 11 и ios 12, потому что iphone x является единственным с надписью вверху и вставкой 44. вот что я действительно обнаруживаю здесь:</p><p><strong>objective-c:</strong></p><pre><code> bool iphonex = no; if (@available(ios 11.0, *)) { uiwindow *mainwindow = [[[uiapplication sharedapplication] delegate] window]; if (mainwindow.safeareainsets.top > 24.0) { iphonex = yes; } } </code></pre><p><strong>swift 4:</strong></p><pre><code>/// 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 } </code></pre><p>и, конечно же, вам может понадобиться проверить левый и правый вкладыши безопасной зоны, если вы находитесь в альбомной ориентации.</p><p>правка: \_window является uiwindow appdelegate, где эта проверка выполняется в приложении didfinishlaunchingwithoptions.</p><p><strong>ответ обновлен для ios 12, чтобы проверить, является ли top> 24, а не top> 0.</strong></p><p>правка: в симуляторе вы можете перейти к hardware, toggle in-call status bar. это показывает, что высота строки состояния не изменяется на iphone x на ios 11 или iphone xs ios 12 при участии в вызове. все, что изменяется, это значок времени, который в обоих случаях получает зеленый фон. вот снимок:</p><p> <a href="https://i.stack.imgur.com/zvvzv.png"> <img src="https://i.stack.imgur.com/zvvzv.png" alt="enter image description here"> </a> </p>

<p>вы должны выполнить различные обнаружения iphone x в зависимости от фактической необходимости.</p><h3>для работы с высшей меткой (статусбар, навигационная панель) и т. д.</h3><pre><code>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 } </code></pre><h3>для работы с нижним индикатором дома (вкладка) и т. д.</h3><pre><code>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 } </code></pre><h3>для размера фона, полноэкранных функций и т. д.</h3><pre><code>class var isiphonexorbigger: bool { // 812.0 on iphone x, xs. // 896.0 on iphone xs max, xr. return uiscreen.main.bounds.height >= 812 } </code></pre><p>примечание: в конечном итоге смешайте его с <code>uidevice.current.userinterfaceidiom == .phone</code><br> примечание: этот метод требует наличия раскадровки launchscreen или соответствующих launchimages</p><h3>для отношения фона, прокрутки и т. д.</h3><pre><code>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 } </code></pre><p>примечание: этот метод требует наличия раскадровки launchscreen или соответствующих launchimages</p><h3>для аналитики, статистики, отслеживания и т. д.</h3><p>получите идентификатор машины и сравните его с документированными значениями:</p><pre><code>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" } </code></pre><p>чтобы включить симулятор в качестве действующего iphone x в аналитику:</p><pre><code>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" } </code></pre><p>чтобы включить iphone xs, xs max и xr, просто найдите модели, начинающиеся с «iphone11»:</p><pre><code>return model == "iphone10,3" || model == "iphone10,6" || model.starts(with: "iphone11,") </code></pre><h3>для поддержки faceid</h3><pre><code>import localauthentication /// will fail if user denies canevaluatepolicy(\_:error:) class var canusefaceid: bool { if #available(ios 11.0, *) { return lacontext().biometrytype == .typefaceid } return false } </code></pre>

<p>вы можете сделать это, чтобы обнаружить <strong>iphone x</strong> устройство в соответствии с размером.</p><p><strong>swift</strong></p><pre><code>if uidevice().userinterfaceidiom == .phone && uiscreen.main.nativebounds.height == 2436 { //iphone x } </code></pre><p><strong>цель - c</strong></p><pre><code>if ([uidevice currentdevice].userinterfaceidiom == uiuserinterfaceidiomphone && uiscreen.mainscreen.nativebounds.size.height == 2436) { //iphone x } </code></pre><p> <a href="https://i.stack.imgur.com/xx17z.png"> <img src="https://i.stack.imgur.com/xx17z.png" alt="enter image description here"> </a> </p><p><strong>но</strong>,</p><p>это не достаточный способ. что делать, если apple анонсировала следующий iphone с таким же размером iphone x. поэтому лучший способ - использовать аппаратную строку для обнаружения устройства.</p><p>для более нового устройства аппаратная строка указана ниже.</p><p>iphone 8 - <strong>iphone10,1</strong> или <strong>iphone 10,4</strong></p><p>iphone 8 plus - <strong>iphone10,2</strong> или <strong>iphone 10,5</strong></p><p>iphone x - <strong>iphone10,3</strong> или <strong>iphone10,6</strong></p>

<p>проверьте <a href="https://stackoverflow.com/a/11197770/1677041">модель устройства/имя машины</a> , не используйте количество точек/пикселей в вашем коде напрямую, это <a href="https://en.wikipedia.org/wiki/hard\_coding"><strong>hard code</strong></a> и не имеет смысла для оборудования устройства.</p><pre><code>#import <sys/utsname.h> nsstring* devicename() { struct utsname systeminfo; uname(&systeminfo); return [nsstring stringwithcstring:systeminfo.machine encoding:nsutf8stringencoding]; } </code></pre><p>результат:</p><pre><code>@"iphone10,3" on iphone x (cdma) @"iphone10,6" on iphone x (gsm) </code></pre><p>обратитесь к <a href="https://stackoverflow.com/a/11197770/1677041">этот ответ</a> .</p><p><strong>полная реализация кода:</strong></p><pre><code>#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) { nsstring *strmodelid = getdevicemodel(); return [strmodelid isequaltostring:@"iphone10,3"] || [strmodelid isequaltostring:@"iphone10,6"] || // iphone x [strmodelid isequaltostring:@"iphone11,2"] || [strmodelid isequaltostring:@"iphone11,4"] || [strmodelid isequaltostring:@"iphone11,6"] || // iphone xs (max) [strmodelid isequaltostring:@"iphone11,8"]; // iphone xr } </code></pre>

<pre><code>#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) </code></pre><h1>определить is\_iphone\_x (is\_iphone && [[uiscreen mainscreen] bounds] .size.height == 812.0)</h1><pre><code>#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"] </code></pre><p>примечание: - будьте осторожны, он отлично работает только для портретной ориентации</p>

<p>посмотрев на все ответы, я сделал следующее:</p><h1>решение (совместимо с swift 4.1)</h1><pre><code>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 } } </code></pre><h1>использование</h1><pre><code>if uidevice.isiphonex { // is iphonex } else { // is not iphonex } </code></pre><h1>заметка</h1><p><strong>pre</strong> swift 4.1 вы можете проверить, работает ли приложение на симуляторе, например:</p><pre><code>target\_os\_simulator != 0 </code></pre><p>начиная с версии swift 4.1 и далее, вы можете проверить, работает ли приложение на симуляторе, используя <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0190-target-environment-platform-condition.md">условие платформы целевой среды</a> :</p><pre><code>#if targetenvironment(simulator) return true #else return false #endif </code></pre><p>(старый метод все еще будет работать, но этот новый метод более перспективен)</p>

<p>все эти ответы, основанные на измерениях, подвержены некорректному поведению на будущих устройствах. они будут работать сегодня, но что, если в следующем году появится iphone того же размера, но с камерой и т.д. под стеклом, чтобы не было «надреза»? если единственный вариант - обновить приложение, тогда это плохое решение для вас и ваших клиентов.</p><p>вы также можете проверить строку аппаратной модели, например «iphone10,1», но это проблематично, потому что иногда apple выпускает разные номера моделей для разных операторов по всему миру.</p><p>правильный подход состоит в том, чтобы перепроектировать верхний макет или решить проблемы, возникающие у вас с настраиваемой высотой панели навигации (это то, на чем я бы сосредоточился). но, если вы решите не делать ни одну из этих вещей, поймите, что все, что вы делаете, - это взлом, чтобы заставить это работать<strong> <em>today</em> </strong>, и вам необходимо исправить это в какой-то момент возможно, несколько раз, чтобы хаки работали.</p>

<p><strong><em>быстрый ответ 4+</em></strong></p><p><strong>iphone x, xr, xs, xsmax: </strong></p><p><em>примечание: для тестирования требуется реальное устройство</em></p><p> <a href="https://stackoverflow.com/a/26962452/7576100">ссылка</a> </p><pre><code> 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 } } </code></pre>

<p>да, это возможно. загрузите расширение <a href="https://github.com/erichoracek/uidevice-hardware">uidevice-hardware</a> (или установите через cocoapod 'uidevice-hardware') и затем используйте:</p><pre><code>nsstring* modelid = [[[uidevice currentdevice] modelidentifier]; bool isiphonex = [modelid isequaltostring:@"iphone10,3"] || [modelid isequaltostring:@"iphone10,6"]; </code></pre><p>обратите внимание, что это не будет работать в симуляторе, только на реальном устройстве.</p>

<p>я знаю, что это только<strong> swift </strong>решение, но оно может кому-то помочь.</p><p>у меня есть <code>globals.swift</code> в каждом проекте, и я всегда добавляю <code>devicetype</code>, чтобы легко определять устройство пользователя:</p><pre><code>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 } } </code></pre><p>тогда использовать это:</p><pre><code>if devicetype.hasnotch { print("this executes on all phones with a notch") } if devicetype.iphone678 { print("this executes on iphones 6, 7 and 8") } </code></pre><p>если вы используете <code>launchimage</code> в своем проекте, обязательно добавьте изображения для всех поддерживаемых устройств (например, xs max, xr), потому что <code>uiscreen.main.bounds</code> не вернет правильное значение без них.</p>

<p><strong> swift 4 </strong>многоразовый<strong> удлинитель </strong></p><pre><code> 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\_max case iphone\_xr } public var hashomebutton: bool { switch type { case .iphone\_x\_xs, .iphone\_xr, .iphone\_xs\_max: 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\_max case 1792: return .iphone\_xr default: return .iphone\_unknown } } return .ipad } } </code></pre>

<p>согласно ответу @ saswanb, это версия swift 4:</p><pre><code>var iphonex = false if #available(ios 11.0, *) { if ((uiapplication.shared.keywindow?.safeareainsets.top)! > cgfloat(0.0)) { iphonex = true } } </code></pre>

<p>все ответы, которые используют <code>height</code>, являются только половиной истории по одной причине. если вы собираетесь выполнить такую проверку, когда ориентация устройства имеет значение <code>landscapeleft</code> или <code>landscaperight</code>, проверка не будет выполнена, поскольку <code>height</code> заменяется на <code>width</code>.</p><p>вот почему мое решение выглядит так в swift 4.0:</p><pre><code>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 } } </code></pre>

<p>не следует полагать, что единственным устройством, которое apple выпускает с другой высотой uinavigationbar, будет iphone x. попробуйте решить эту проблему, используя более общее решение. если вы хотите, чтобы полоса всегда была на 20px больше высоты по умолчанию, ваш код должен добавить 20px к высоте полосы, вместо того, чтобы устанавливать ее на 64px (44px + 20px).</p>

<p><strong> swift 3 + 4: </strong></p><blockquote> <p>без необходимости какого-либо размера пикселя устройства</p> </blockquote><pre><code>//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 } } </code></pre><p>пример:</p><pre><code>if uiapplication.isdevicewithsafearea { //e.g. change the frame size height of your uitabbar } </code></pre>

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

<pre><code>#define is\_iphone (ui\_user\_interface\_idiom() == uiuserinterfaceidiomphone) #define is\_iphone\_x (is\_iphone && [[uiscreen mainscreen] bounds].size.height == 812.0f) </code></pre>

<p>обычно это нужно программисту для ограничения сверху или снизу, поэтому эти методы могут помочь</p><pre><code>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 } </code></pre><p>для до iphone x эти методы возвращают: 0</p><p>для iphone x: 44 и 34 соответственно</p><p>затем просто добавьте эти дополнения к верхним или нижним ограничениям</p>

<p>для тех, кто получает 2001px вместо 2436px для собственной высоты границ (как я), это потому, что вы создали свое приложение с более старым sdk, до ios 11 (xcode 8 вместо xcode 9). в более старом sdk ios будет отображать приложения «в черной рамке» на iphone x вместо того, чтобы расширять экран от края до края за верхнюю «отметку сенсора». это уменьшает размер экрана, поэтому это свойство возвращает 2001 вместо 2436.</p><p>самое простое решение - просто проверить оба размера, если вас интересует только обнаружение устройства. я использовал этот метод для определения faceid при сборке со старым xcode sdk, который не имеет значения enum, указывающего биометрический тип. в этой ситуации обнаружение устройства с использованием высоты экрана казалось лучшим способом узнать, было ли у устройства faceid против touchid без необходимости обновления xcode.</p>

<pre><code>- (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; } } </code></pre>

<p>я подробно остановился на ваших ответах и сделал расширение swift для uidevice. мне нравятся swift перечисления и "все в порядке" и атомизированные. я создал решение, которое работает как на устройстве, так и на симуляторе. </p><p>преимущества: - простой интерфейс, использование, например <code>uidevice.current.isiphonex</code> - <code>uidevicemodeltype</code> enum дает вам возможность легко расширять специфические для модели функции и константы, которые вы хотите использовать в своем приложении, например, cornerradius</p><p>недостаток: - это решение для конкретной модели, а не для конкретного разрешения, например если apple выпустит другую модель с такими же характеристиками, это не будет работать правильно, и вам нужно добавить другую модель, чтобы эта работа работала => вам необходимо обновить приложение.</p><pre class="lang-swift prettyprint-override"><code>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 } } </code></pre>

<p>не используйте размер пикселя экрана, как предлагали другие решения, это плохо, поскольку это может привести к ложным срабатываниям для будущих устройств; не будет работать, если uiwindow еще не визуализировал (appdelegate), не будет работать в ландшафтных приложениях и может не работать на симуляторе, если установлен масштаб.</p><p>вместо этого я создал макрос для этой цели, он очень прост в использовании и использует аппаратные флаги для предотвращения вышеупомянутых проблем.</p><p>правка: обновлено для поддержки iphonex, iphone xs, iphonexr, iphonexs max</p><hr><h1>использовать:</h1><pre><code>if (is\_device\_iphonex) { //do stuff } </code></pre><p>да, действительно. </p><hr><h1>macro:</h1><p>просто скопируйте и вставьте это куда угодно, я предпочитаю самый низ моего .h файла после <code>@end</code></p><pre><code>#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]);\ })()\ ) </code></pre>

<p>я полагаюсь на высоту строки состояния строки состояния, чтобы определить, является ли это iphone x:</p><pre><code>if uiapplication.shared.statusbarframe.height >= cgfloat(44) { // it is an iphone x } </code></pre><p>это для приложения и портрета. вы также можете проверить размер в соответствии с ориентацией устройства. кроме того, на других iphone панель состояния может быть скрыта, поэтому высота кадра равна <code>0</code>. на iphone x строка состояния никогда не скрывается.</p>

<p>я использовал <a href="https://stackoverflow.com/a/48298349/8483739">код питера крейнца</a> (потому что он был чистый и делал то, что мне было нужно), но потом я понял, что он работает только тогда, когда устройство находится в портретном режиме (поскольку верхнее заполнение будет сверху, очевидно). создал расширение для обработки всех ориентаций с соответствующими отступами, без учета размера экрана:</p><pre><code>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 } } </code></pre><p>а на сайте вашего звонка вы просто:</p><pre><code>let res = uidevice.current.isiphonex </code></pre>

<p>кроме того, вы можете проверить ' <a href="https://github.com/dennisweissmann/devicekit#get-the-device-youre-running-on">devicekit</a> ' pod . после установки, все, что вам нужно сделать, чтобы проверить устройство, это:</p><pre><code>import devicekit let device = device() if device == .iphonex { // place your code here } </code></pre>

<p>я должен был решить ту же проблему недавно. и хотя на этот вопрос дан окончательный ответ («нет»), это может помочь другим, кому нужно специфическое поведение макета iphone x. </p><p>меня не интересовало, было ли это устройство iphone x. меня интересовало, имеет ли устройство дисплей с надрезом.</p><pre><code>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 } </code></pre><p>вы также можете написать переменную <code>hasonscreenhomeindicator</code> в том же ключе (хотя, возможно, проверьте нижнюю безопасную область?).</p><p>выше используется мое расширение на <code>uiview</code> для удобного доступа к вставкам безопасной области на ios 10 и более ранних версиях. </p><pre><code>@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 } } } </code></pre>

<p>есть несколько причин, чтобы узнать, что это за устройство.</p><ol> <li><p>вы можете проверить высоту устройства (и ширину). это полезно для макета, но вы обычно не хотите делать это, если хотите знать точное устройство.</p></li> <li><p>для макета вы также можете использовать <code>uiview.safeareainsets</code>.</p></li> <li><p>если вы хотите отобразить имя устройства, например, для включения в электронное письмо для диагностических целей, после извлечения модели устройства с помощью <code>sysctl ()</code>, вы можете использовать эквивалент этого, чтобы вычислить имя:</p> <pre><code>$ curl http://appledevicenames.com/devices/iphone10,6 iphone x </code></pre></li> </ol>

<p>я думаю, что<strong> apple не хочет, чтобы мы вручную проверяли, есть ли на устройстве "метка" или "домашний индикатор" </strong>но работает код:</p><pre><code>-(bool)hastopnotch{ if (@available(ios 11.0, *)) { float max\_safe\_area\_inset = max(max([[[uiapplication sharedapplication] delegate] window].safeareainsets.top, [[[uiapplication sharedapplication] delegate] window].safeareainsets.right),max([[[uiapplication sharedapplication] delegate] window].safeareainsets.bottom, [[[uiapplication sharedapplication] delegate] window].safeareainsets.left)); return max\_safe\_area\_inset >= 44.0; } return no; } -(bool)hashomeindicator{ if (@available(ios 11.0, *)) { int inumbersafeinsetsequalzero = 0; if([[[uiapplication sharedapplication] delegate] window].safeareainsets.top == 0.0)inumbersafeinsetsequalzero++; if([[[uiapplication sharedapplication] delegate] window].safeareainsets.right == 0.0)inumbersafeinsetsequalzero++; if([[[uiapplication sharedapplication] delegate] window].safeareainsets.bottom == 0.0)inumbersafeinsetsequalzero++; if([[[uiapplication sharedapplication] delegate] window].safeareainsets.left == 0.0)inumbersafeinsetsequalzero++; return inumbersafeinsetsequalzero <= 2; } return no; } </code></pre><p>некоторые другие посты не работают. например, iphone 6s с «строкой состояния в режиме разговора» (зеленая полоса) в портретном режиме имеет безопасную вставку с большим верхом. с моим кодом все дела рассматриваются (<strong> даже если устройство запускается в портретной или альбомной ориентации </strong>)</p>

<p>лучший и самый простой способ определить, является ли устройство iphone x,</p><p> <a href="https://github.com/stephanheilner/uidevice-displayname">https://github.com/stephanheilner/uidevice-displayname</a> </p><pre><code>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)))} </code></pre><p>и идентификатор это либо <strong>"iphone10,3", либо "iphone10,6"</strong> для iphone x.</p>

<p>в <strong>portrait only</strong> я использую ширину и высоту рамки вида для проверки:</p><pre><code>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") } } </code></pre><p> <a href="https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions">размеры портретного экрана перечислены здесь</a> </p><p> <a href="https://i.stack.imgur.com/tmxs3.png"> <img src="https://i.stack.imgur.com/tmxs3.png" alt="enter image description here"> </a> </p>

<p>с выпуском ios 12 такие устройства, как iphone x, могут попасть в эту категорию. </p><p>` </p><pre><code>extension uidevice { var isportrait: bool { return uideviceorientationisportrait(orientation) || uiinterfaceorientationisportrait(uiapplication.shared.statusbarorientation) } var isdevicewith\_xshape : bool { if self.userinterfaceidiom == .phone { if isportrait { switch uiscreen.main.nativebounds.height { case 2436,2688,1792: print("iphone x, xs, xr, xs max") return true default: print("any other device") return false } } else { switch uiscreen.main.nativebounds.width { case 2436,2688,1792: print("iphone x, xs, xr, xs max") return true default: print("any other device") return false } } } else { return false } }` </code></pre>

<p>для быстрого решения мне нравится это:</p><pre><code>let var:cgfloat = (uidevice.current.userinterfaceidiom == .phone && uiscreen.main.nativebounds.height == 2436) ? <iphonex> : <allothers> </code></pre>

<p>чтобы обнаружить любое из устройств, используя простые методы. как ниже,</p><pre><code>func isphonedevice() -> bool { return uidevice.current.userinterfaceidiom == .phone } func isdeviceipad() -> bool { return uidevice.current.userinterfaceidiom == .pad } func ispadprodevice() -> bool { let screen\_width: cgfloat = uiscreen.main.bounds.size.width let screen\_height: cgfloat = uiscreen.main.bounds.size.height let screen\_max\_length: cgfloat = fmax(screen\_width, screen\_height) return uidevice.current.userinterfaceidiom == .pad && screen\_max\_length == 1366.0 } func isphonexandxsdevice() -> bool { let screen\_width = cgfloat(uiscreen.main.bounds.size.width) let screen\_height = cgfloat(uiscreen.main.bounds.size.height) let screen\_max\_length: cgfloat = fmax(screen\_width, screen\_height) return uidevice.current.userinterfaceidiom == .phone && screen\_max\_length == 812.0 } func isphonexsmaxandxrdevice() -> bool { let screen\_width = cgfloat(uiscreen.main.bounds.size.width) let screen\_height = cgfloat(uiscreen.main.bounds.size.height) let screen\_max\_length: cgfloat = fmax(screen\_width, screen\_height) return uidevice.current.userinterfaceidiom == .phone && screen\_max\_length == 896.0 } </code></pre><p>и звони так,</p><pre><code>if isphonedevice() { // your code } </code></pre>