How to show in SwiftUI the sidebar in iPad and portrait mode

1.5k Views Asked by At

I have an master detail app in iPad, and when run the app in portrait mode the sidebar is hidden. I need to push Back button to open the sidebar.

Can anyone help me to show the sidebar by default? I found an answer that suggest to use StackNavigationViewStyle when the app is in portrait, but then the app seems like a giant iPhone and dissapears the master class like a sidebar to appear like a view.

Thats my code.

struct ContentView: View {
    var body: some View {
        NavigationView {
            MyMasterView()
            DetailsView()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct MyMasterView: View {

    var people = ["Option 1", "Option 2", "Option 3"]

    var body: some View {

        List {
            ForEach(people, id: \.self) { person in
                NavigationLink(destination: DetailsView()) {
                    Text(person)
                }
            }
        }

    }
}

struct DetailsView: View {

    var body: some View {
        Text("Hello world")
            .font(.largeTitle)
    }
}

Thank you

2

There are 2 best solutions below

0
On BEST ANSWER

It can be done, but for now it requires access to UIKit's UISplitViewController via UIViewRepresentable. Here's an example, based on a solution described here.

import SwiftUI
import UIKit

struct UIKitShowSidebar: UIViewRepresentable {
  let showSidebar: Bool
  
  func makeUIView(context: Context) -> some UIView {
    let uiView = UIView()
    if self.showSidebar {
      DispatchQueue.main.async { [weak uiView] in
        uiView?.next(of: UISplitViewController.self)?
          .show(.primary)
      }
    } else {
      DispatchQueue.main.async { [weak uiView] in
        uiView?.next(of: UISplitViewController.self)?
          .show(.secondary)
      }
    }
    return uiView
  }
  
  func updateUIView(_ uiView: UIViewType, context: Context) {
    DispatchQueue.main.async { [weak uiView] in
      uiView?.next(
        of: UISplitViewController.self)?
        .show(showSidebar ? .primary : .secondary)
    }
  }
}

extension UIResponder {
  func next<T>(of type: T.Type) -> T? {
    guard let nextValue = self.next else {
      return nil
    }
    guard let result = nextValue as? T else {
      return nextValue.next(of: type.self)
    }
    return result
  }
}


struct ContentView: View {
  var body: some View {
    NavigationView {
      List {
        NavigationLink("Primary view (a.k.a. Sidebar)", destination: DetailView())
      }
      NothingView()
    }
  }
}

struct DetailView: View {
  var body: some View {
    Text("Secondary view (a.k.a Detail)")
  }
}

struct NothingView: View {
  @State var showSidebar: Bool = false
  var body: some View {
    Text("Nothing to see")
    if UIDevice.current.userInterfaceIdiom == .pad {
      UIKitShowSidebar(showSidebar: showSidebar)
        .frame(width: 0,height: 0)
        .onAppear {
            showSidebar = true
        }
        .onDisappear {
            showSidebar = false
        }
    }
  }
}
0
On

In iOS 16 you'll be able to control it using columnVisibility