Often the UI of iOS applications is only designed for screens of one size. For example, iPhone X (375x812) or iPhone 13 (390x844), and then iOS developers make designs for screens of other sizes.
With large screens, everything is simple. But placing the design of the iPhone X on the screen of the iPhone SE 2016 (1st gen.) is more difficult, some elements and indents have to be reduced manually.
How to scale - Simple implementation
We go through the UIView hierarchy and scale (almost) all the constraints.
extension UIView {
func callRecursively(level: Int = 0, _ body: (_ subview: UIView, _ level: Int) -> Void) {
body(self, level)
subviews.forEach { $0.callRecursively(level: level + 1, body) }
}
func scale() {
// 390 is an iPhone 13 screen width
var scaleFactor = min(UIScreen.main.bounds.width / 390, 1)
callRecursively { (subview, level) in
// to avoid scalе of system constraints
guard level != 0 else { return }
for constraint in subview.constraints {
if constraint.constant != 0 {
constraint.constant = CGFloat(constraint.constant) * scaleFactor
}
}
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// one line is enough
view.scale()
}
}
func scale(kFor5: CGFloat? = nil, kFor8: CGFloat? = nil) {
// 390 is an iPhone 13 screen width
// 375 is for iPhone Х
// and 320 is for iPhone SE 2016 (1st gen.)
var scaleFactor = min(UIScreen.main.bounds.width / 390, 1)
if let kFor5 = kFor5,
UIScreen.main.bounds.width <= 320 {
scaleFactor = scaleFactor * kFor5
} else if let kFor8 = kFor8,
UIScreen.main.bounds.width <= 375,
UIScreen.main.bounds.height <= 667 {
scaleFactor = scaleFactor * kFor8
}
callRecursively { (subview, level) in
// to avoid scalе of system constraints
guard level != 0 else { return }
if let label = subview as? UILabel {
label.scaleFont(scaleFactor)
} else if let button = subview as? UIButton {
button.scaleFont(scaleFactor)
}
for constraint in subview.constraints {
if constraint.constant != 0 {
constraint.constant = CGFloat(constraint.constant) * scaleFactor
}
}
}
}
extension UILabel {
func scaleFont(_ k: CGFloat) {
let fontSize = font.pointSize
font = font.withSize(fontSize * k)
}
}
extension UIButton {
func scaleFont(_ k: CGFloat) {
let fontSize: Double = Double(titleLabel?.font.pointSize ?? 0.0)
titleLabel?.font = titleLabel?.font.withSize(fontSize * k)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// you can play with those factors for best results
view.scale(kFor5: 0.75, kFor8: 0.85)
}
}
That's all. Please write if you have any comments, I'm almost sure it can be improved further. Also, please let me know if you have solved a similar problem.