Итак, первый классический - обычный 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. И практически никогда третий вариант.
Экспериментируйте! Если найдёте для себя полезным тот или иной вариант, попробуйте его в своём проекте. Если я упустил что-то, не стесняйтесь поделиться этим в комментариях.
Надеюсь, эта статья была для вас полезна.
