view.window?.windowScene?.screen.bounds instead of the deprecated UIScreen.main. In SwiftUI, avoid manual screen calculations by using GeometryReader or .containerRelativeFrame(.horizontal) { length, axis in length * 0.75 }.Why this happens
Historically, iOS developers relied on UIScreen.main.bounds to calculate widths and heights for interface elements. However, Apple deprecated UIScreen.main in iOS 16. On modern versions of iOS and iPadOS, an app does not always occupy the entire physical display. Features like Split View, Slide Over, Stage Manager, and external display support mean your app’s window often differs significantly from the hardware screen size.
Relying on raw screen dimensions also introduces layout bugs across device rotations. When a device rotates, physical screen dimensions do not automatically update custom views that cached width and height at launch. In modern Swift code, view sizing should be determined by window bounds, view hierarchies, or container frames rather than physical device boundaries.
How to fix it
1. Read window bounds via UIWindowScene in UIKit
If you are writing UIKit code and need the actual screen or window dimensions, read them from the active UIWindowScene attached to your view hierarchy instead of calling UIScreen.main.
Inside a UIViewController:
import UIKit
class SampleViewController: UIViewController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// The view's own bounds represent the available space for your layout
let availableWidth = view.bounds.width
let availableHeight = view.bounds.height
// If you specifically need the physical screen size of this window's display:
if let screenBounds = view.window?.windowScene?.screen.bounds {
let screenWidth = screenBounds.width
let screenHeight = screenBounds.height
print("Screen width: \(screenWidth), height: \(screenHeight)")
}
}
}
If you need screen dimensions in a standalone helper method where no UIView reference is directly available, query the connected scenes from UIApplication:
import UIKit
func currentScreenBounds() -> CGRect? {
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first { $0.activationState == .foregroundActive } as? UIWindowScene
return windowScene?.screen.bounds
}
2. Use containerRelativeFrame or GeometryReader in SwiftUI
If you want an element to occupy 75% of the visible container in SwiftUI, do not query screen dimensions manually. For apps targeting iOS 17 and later, use .containerRelativeFrame:
import SwiftUI
struct ProportionalButtonView: View {
var body: some View {
Button("Continue") {
// Action
}
.buttonStyle(.borderedProminent)
.containerRelativeFrame(.horizontal) { length, axis in
length * 0.75
}
}
}
For backwards compatibility with iOS 16 and earlier, wrap the layout in a GeometryReader:
import SwiftUI
struct ProportionalLayoutView: View {
var body: some View {
GeometryReader { geometry in
VStack {
Button("Continue") {
// Action
}
.frame(width: geometry.size.width * 0.75)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
3. Use proportional Auto Layout constraints in UIKit
Calculating view frames as screenWidth * 0.75 in UIKit breaks when orientation changes or when running in iPad multitasking. Use Auto Layout with dimension multipliers instead:
import UIKit
class LayoutHelper {
static func addProportionalButton(to parentView: UIView) -> UIButton {
let button = UIButton(type: .system)
button.setTitle("Submit", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
parentView.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: parentView.centerXAnchor),
button.centerYAnchor.constraint(equalTo: parentView.centerYAnchor),
// Sets the button width to exactly 75% of its parent view
button.widthAnchor.constraint(equalTo: parentView.widthAnchor, multiplier: 0.75),
button.heightAnchor.constraint(equalToConstant: 50)
])
return button
}
}
How to prevent it
- Do not use
UIScreen.main.boundsfor view sizing; compiler warnings flag this as deprecated in modern SDKs. - Prefer layout multipliers via Auto Layout constraints over manual frame math in UIKit.
- Use
view.boundsinsideviewDidLayoutSubviews()if you must calculate frames manually, asboundsupdates automatically when orientation changes. - Rely on SwiftUI container tools (
GeometryReaderor.containerRelativeFrame) to adapt cleanly to iPad Split View and macOS Catalyst.
FAQ
Why is UIScreen.main deprecated in iOS 16?
Apple deprecated UIScreen.main because iOS devices can support multiple connected displays and multiple concurrent windows. An app can no longer assume there is only one “main” screen.
How do I get safe area insets along with screen size?
In UIKit, inspect view.safeAreaInsets inside viewDidLayoutSubviews(). In SwiftUI, safe areas are handled automatically, but you can read exact insets using geometry.safeAreaInsets inside a GeometryReader.
Does screen size change when the user rotates the device?
Yes. The width and height values swap when transitioning between portrait and landscape modes. Reading dimensions inside lifecycle methods like viewDidLoad will yield stale data after rotation.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
