I am trying to present a view in landscape orientation from a portrait view using the below code
Button {
isLandscapeViewPresented.toggle()
} label: {
Text("Click for landscape view")
}.fullScreenCover(isPresented: $isLandscapeViewPresented) {
LandscapeOnlyView {
LandscapeView()
}
}
And in LandscapeOnlyView wrapper I have as below
struct LandscapeOnlyView<Content: View>: UIViewControllerRepresentable {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }
func makeUIViewController(context: Context) -> some UIViewController {
return LandscapeHostingController(rootView: content)
}
}
For LandscapeHostingController which holds the rootview as below:
class LandscapeHostingController<Content: View>: UIHostingController<Content> {
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .landscape
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AppDelegate.orientationLock = .landscape
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
scene?.requestGeometryUpdate(.iOS(interfaceOrientations: .landscapeRight), errorHandler: { error in
print(error)
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
AppDelegate.orientationLock = .portrait
let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
scene?.requestGeometryUpdate(.iOS(interfaceOrientations: .portrait), errorHandler: { error in
print(error)
})
}
}
I am using AppDelegate, as you can see where I update the orientationLock, with supportedInterfaceOrientations override returning the updated orientation whenever I need to change orientations.
I am getting the below output.
I need the LandscapeView to be in landscape even before presenting unlike what is show above where the view switches to landscape as the orientation update is done in viewWillAppear of the LandscapeHostingController wrapper for a swift ui view.
I have tried many ways. Is there something wrong in my approach which should be changed.
Any ideas on how that it be achieved?
2
Answers
To lock the orientation of a view to only landscape in a SwiftUI app, you need to manage the orientation in a
UIViewController
or via scene-based updates. Below is a simplified approach that ensures the view is locked in landscape before it appears.Code Example:
This ensures that the
LandscapeView
locks into the landscape orientation before it appears, addressing the issue you described.This is difficult to do in general. SwiftUI doesn’t directly respect the
supportedInterfaceOrientations
ofUIViewControllerRepresentable
view controllers, but you can (kind of) fool it into doing so by presenting another full-screenUIViewController
from your representable controller. You’ll have to remove some of yourrequestGeometryUpdate
calls as well. Something like this:This will have a small delay before presenting the content, but it will at least get rid of the unwanted rotation animation.