swiftuiipadipados

How do I remove the sidebar toggle from a NavigationSplitView on iPad only in landscape mode


How do I remove the sidebar toggle from a NavigationSplitView on iPad in landscape mode but keep it in portrait mode? Similar to how it works in the home app in iOS 17.

I have this so far but it doesn't work correctly at first launch before switching orientation once:

struct SidebarMainView: View {
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    @State private var isPortrait = true

    var body: some View {
        NavigationSplitView {
            List {
                Text("1")
                Text("2")
                Text("3")
            }
            .listStyle(SidebarListStyle()).toolbar(removing: isPortrait ? .none : .sidebarToggle).navigationTitle("HP").onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                guard let scene = UIApplication.shared.windows.first?.windowScene else { return }
                self.isPortrait = scene.interfaceOrientation.isPortrait
            }
        } detail: {
            Text("Select a department: \(self.isPortrait ? "yes" : "no")")
        }
    }
}

For reference, this is what the Home app looks like in different layouts. Note the forced and optional sidebar in each layout. I want to accomplish the same.

enter image description here enter image description here enter image description here enter image description here enter image description here


Solution

  • For iOS 17+
    We can use UIDevice.orientationDidChangeNotification to detect current orientation and toolbar(removing:) for removing sidebar toggle button.

    struct ContentView: View {
        @State private var deviceOrientation = UIDevice.current.orientation
    
        var body: some View {
            NavigationSplitView {
                Text("sidebar")
                    .detectOrientation($deviceOrientation)
                    // After removing sidebar toggle, it was not adding when refreshing (Instead Visibility solves the problem)
                    // .toolbar(removing:  isOrientationPortrait() ? .none : .sidebarToggle)
                    .toolbar(isOrientationPortrait() ? .visible : .hidden, for: .navigationBar)
            } detail: {
                Text("Detail")
            }
        }
    
        private func isOrientationPortrait() -> Bool {
            return deviceOrientation == .portrait || deviceOrientation == .portraitUpsideDown
        }
    }
    
    
    extension View {
        func detectOrientation(_ orientation: Binding<UIDeviceOrientation>) -> some View {
            modifier(DetectOrientation(orientation: orientation))
        }
    }
    
    struct DetectOrientation: ViewModifier {
        
        @Binding var orientation: UIDeviceOrientation
        
        func body(content: Content) -> some View {
            content.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                    orientation = UIDevice.current.orientation
                }
        }
    }
    

    Hope It Helps !