Итак, первый классический - обычный NSLayoutConstraint
. Удобный, нативный и нисколько не обременяющий в написании. Но что если ваше приложение должно работать на iPhone SE 1 поколения? Тогда с вероятностью в 100% где-то вёрстка поедет. Для этого случая вы можете использовать UIDevice.current.
Второй помощник - UIDevice.current
. Эта переменная, которую вы можете сами прописать в extension
UIDevice.
Она позволяет вам высчитывать размеры текущего устройства и исходя из этого создавать другие переменные и делегировать устройства по группам: таким, как isSmallScreen
/isXScreenDevice
. Но даже этого не всегда бывает достаточно, и приходится отказываться от разного рода дизайнерских решений.
extension UIDevice {
var iPhone: Bool {
return UIDevice().userInterfaceIdiom == .phone
}
enum ScreenType: String {
case iPhone8
case iPhone8Plus
case iPhoneX
case iPhoneXSMax
case iPhone11
case iPhone11Pro
case iPhoneSE
case iPhone12
case iPhone12Pro
case iPhone12Mini
case Unknown
}
var current: ScreenType {
guard iPhone else { return .Unknown}
switch UIScreen.main.nativeBounds.height {
case 1136:
return .iPhoneSE
case 1334:
return .iPhone8
case 2208:
return .iPhone8Plus
case 2436:
return .iPhoneX
case 2521:
return .iPhone12
case 2532:
return .iPhone11Pro
case 2688:
return .iPhoneXSMax
case 2778:
return .iPhone12Pro
case 1792:
return .iPhone11
default:
return .Unknown
}
}
}
И третий, о котором я узнал лишь на первой работе и больше нигде его не применял. Для этого мы создаём константы screenHeight
и screenWidth
, которые равны UIScreen.main.bounds.height/width
. При использовании в NSLayoutConstraint
придерживаемся такой формулировки:
// Для вертикальных констрейнтов (например topAnchor, bottomAnchor)
20/812*screenHeight
// Для горизонтальных констрейнтов (например leadingAnchor, trailingAnchor)
20/375*screenWidth
Этот вариант практически идеально позволяет распределить UI-объекты на разных устройствах, и элементы вашего приложения будут выглядеть всегда одинаково и на iPhone 14 Pro Max, и на iPhone SE.
При первом взгляде это самый подходящий вариант. Однако чем чаще его используешь, тем больше приходит понимание, что это не панацея, а скорее не нужный, объёмный "костыль", который заполоняет весь код магическими числами и непременно вызовет негодование у вашего тимлида. Поэтому я бы рекомендовал его использовать только в редких случаях.
NSLayoutConstraint.activate([
secondTitleLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -156/812*screenHeight),
secondTitleLabel.rightAnchor.constraint(equalTo: view.rightAnchor),
secondTitleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
secondTitleLabel.leftAnchor.constraint(equalTo: view.leftAnchor)
])
NSLayoutConstraint.activate([
mainTitleLabel.bottomAnchor.constraint(equalTo: secondTitleLabel.topAnchor, constant: -32/375*screenWidth),
mainTitleLabel.rightAnchor.constraint(equalTo: view.rightAnchor),
mainTitleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
mainTitleLabel.leftAnchor.constraint(equalTo: view.leftAnchor)
])
В заключение скажу, что спустя время я чаще прибегаю к обычной работе констрейнтов, и их всегда хватает с головой. Иногда в редких случаях я использую UIDevice.current
. И практически никогда третий вариант.
Экспериментируйте! Если найдёте для себя полезным тот или иной вариант, попробуйте его в своём проекте. Если я упустил что-то, не стесняйтесь поделиться этим в комментариях.
Надеюсь, эта статья была для вас полезна.