swiftswiftuiorientation

In SwiftUI, how do you perform a code before the interface orientation change?


I need to execute a code before the device's interface starts rotating but I can't seem to find any viable solution.

I came across UIDevice.orientationDidChangeNotification but couldn't find anything like that to be notified that an orientation change will start.

I tried creating an UIViewControllerRepresentable with a custom UIViewController that overrides func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator), but that function doesn't get invoked at all (I didn't forget to propagate it using super).

Any idea?

EDIT: Needs support for iOS 15.0+

EDIT 2: This is needed in a SwiftUI app (so not UIKit)


Solution

  • If you create a regular UIViewController that post's a Notification you can onReceive the notification after you subscribe.

    import SwiftUI
    
    struct TransitionNotificationView: View {
        typealias SizeData = TransitionVM.SizeData
        var body: some View {
            Text("Hello, World!")
                .subscribeToTransition() //subscribe
                .onReceive(NotificationCenter.default.publisher(for: .willTransitionSize), perform: { notification in
                    guard let object = notification.object as? SizeData else {return}
    
                    print("received size Transision \(object.size.debugDescription)")
                })
        }
    }
    #Preview {
        TransitionNotificationView()
    }
    

    The code behind this is pretty standard.

    extension View {
        func subscribeToTransition() -> some View {
            modifier(TransitionVM())
        }
    }
    struct TransitionVM: ViewModifier {
        typealias TraitData = Transition_UI.TraitData
        typealias SizeData = Transition_UI.SizeData
        func body(content: Content) -> some View {
            content.background(Transition_UI().frame(width: 0, height: 0))
        }
        
        struct Transition_UI: UIViewControllerRepresentable {
            typealias TraitData = (newCollection: UITraitCollection, coordinator: UIViewControllerTransitionCoordinator)
            typealias SizeData = (size: CGSize, coordinator: UIViewControllerTransitionCoordinator)
            func makeUIViewController(context: Context) -> some UIViewController {
                TransitionVC(nibName: nil, bundle: nil)
            }
            func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
                
            }
            class TransitionVC: UIViewController {
                ///UIKit calls this method before changing the size of a presented view controller’s view.
                override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
                    print(#function)
                    super.viewWillTransition(to: size, with: coordinator)
                    NotificationCenter.default.post(name: .willTransitionSize, object: (size, coordinator))
                    
                }
                ///UIKit calls this method before changing the current object’s **traits**
                override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
                    super.willTransition(to: newCollection, with: coordinator)
                    print(#function)
                    NotificationCenter.default.post(name: .willTransitionTrait, object: (newCollection, coordinator))
                }
            }
        }
    }
    
    extension Notification.Name {
    
        static let willTransitionTrait = Notification.Name("willTransitionTrait")
        static let willTransitionSize = Notification.Name("willTransitionSize")
    
    }