Lock screen orientation per-view in SwiftUI. No UIKit subclassing, no hacks.
Text("This view stays portrait")
.supportedInterfaceOrientations(.portrait)That's it. The view locks to portrait. Navigate away, it unlocks. Conflicting constraints across multiple views? Handled automatically.
SwiftUI has no native way to lock orientation for specific views. You either lock the entire app in Info.plist, or dive into UIKit lifecycle callbacks scattered across AppDelegate, SceneDelegate, and view controllers.
This package gives you a single view modifier that just works.
dependencies: [
.package(url: "https://github.com/ivan-magda/swiftui-interface-orientation.git", from: "1.2.0")
]Or in Xcode: File → Add Packages → paste the URL above.
import SwiftUIInterfaceOrientation
@main
struct MyApp: App {
init() {
InterfaceOrientationManager.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}class OrientationDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
supportedInterfaceOrientationsFor window: UIWindow?
) -> UIInterfaceOrientationMask {
InterfaceOrientationManager.shared.supportedInterfaceOrientations
}
}
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(OrientationDelegate.self) var delegate
init() {
InterfaceOrientationManager.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}struct PortraitOnlyView: View {
var body: some View {
Text("Locked to portrait")
.supportedInterfaceOrientations(.portrait)
}
}
struct LandscapeOnlyView: View {
var body: some View {
VideoPlayer(player: player)
.supportedInterfaceOrientations([.landscapeLeft, .landscapeRight])
}
}The package uses a centralized InterfaceOrientationManager that tracks orientation constraints from all active views:
- When a view with
.supportedInterfaceOrientations()appears, it registers its constraint - When the view disappears, the constraint is removed
- The manager computes the intersection of all active constraints
- If constraints conflict (intersection is empty), it falls back to your app's defaults from Info.plist
This means nested views with different constraints "just work"—the most restrictive common orientation wins.
func supportedInterfaceOrientations(_ orientations: UIInterfaceOrientationMask) -> some View// Use defaults from Info.plist
InterfaceOrientationManager.configure()
// Or specify custom defaults
InterfaceOrientationManager.configure(
configuration: .init(defaultOrientations: .portrait)
).portrait // Portrait only
.landscapeLeft // Landscape, home button on right
.landscapeRight // Landscape, home button on left
.portraitUpsideDown // Upside down (iPad only effectively)
.landscape // Both landscape orientations
.all // All orientations
.allButUpsideDown // All except upside down- iOS 14.0+
- Swift 6
- Xcode 16.0+
MIT License. See LICENSE for details.
Issues and PRs welcome.