Collapse a doubleColumn NavigationView detail in SwiftUI like with collapsed on UISplitViewController?

后端 未结 5 1488
渐次进展
渐次进展 2020-12-24 07:38

So when I make a list in SwiftUI, I get the master-detail split view for \"free\".

So for instance with this:

import SwiftUI

struct ContentView : Vi         


        
5条回答
  •  渐次进展
    2020-12-24 07:49

    For now, in Xcode 11.2.1 it is still nothing changed. I had the same issue with SplitView on iPad and resolved it by adding padding like in Ketan Odedra response, but modified it a little:

    var body: some View {
        GeometryReader { geometry in
            NavigationView {
                MasterView()
                DetailsView()
            }
            .navigationViewStyle(DoubleColumnNavigationViewStyle())
            .padding(.leading, leadingPadding(geometry))
        }
    }
    
    private func leadingPadding(_ geometry: GeometryProxy) -> CGFloat {
        if UIDevice.current.userInterfaceIdiom == .pad {
            return 0.5
        }
        return 0
    }
    

    This works perfectly in the simulator. But when I submit my app for review, it was rejected. This little hack doesn't work on the reviewer device. I don't have a real iPad, so I don't know what caused this. Try it, maybe it will work for you.

    While it doesn't work for me, I requested help from Apple DTS. They respond to me that for now, SwiftUI API can't fully simulate UIKit`s SplitViewController behavior. But there is a workaround. You can create custom SplitView in SwiftUI:

    struct SplitView: View {
        var master: Master
        var detail: Detail
    
        init(@ViewBuilder master: () -> Master, @ViewBuilder detail: () -> Detail) {
            self.master = master()
            self.detail = detail()
        }
    
        var body: some View {
            let viewControllers = [UIHostingController(rootView: master), UIHostingController(rootView: detail)]
            return SplitViewController(viewControllers: viewControllers)
        }
    }
    
    struct SplitViewController: UIViewControllerRepresentable {
        var viewControllers: [UIViewController]
        @Environment(\.splitViewPreferredDisplayMode) var preferredDisplayMode: UISplitViewController.DisplayMode
    
        func makeUIViewController(context: Context) -> UISplitViewController {
            return UISplitViewController()
        }
    
        func updateUIViewController(_ splitController: UISplitViewController, context: Context) {
            splitController.preferredDisplayMode = preferredDisplayMode
            splitController.viewControllers = viewControllers
        }
    }
    
    struct PreferredDisplayModeKey : EnvironmentKey {
        static var defaultValue: UISplitViewController.DisplayMode = .automatic
    }
    
    extension EnvironmentValues {
        var splitViewPreferredDisplayMode: UISplitViewController.DisplayMode {
            get { self[PreferredDisplayModeKey.self] }
            set { self[PreferredDisplayModeKey.self] = newValue }
        }
    }
    
    extension View {
        /// Sets the preferred display mode for SplitView within the environment of self.
        func splitViewPreferredDisplayMode(_ mode: UISplitViewController.DisplayMode) -> some View {
            self.environment(\.splitViewPreferredDisplayMode, mode)
        }
    }
    

    And then use it:

    SplitView(master: {
                MasterView()
            }, detail: {
                DetailView()
            }).splitViewPreferredDisplayMode(.allVisible)
    

    On an iPad, it works. But there is one issue (maybe more..). This approach ruins navigation on iPhone because both MasterView and DetailView have their NavigationView.

    UPDATE: Finally, in Xcode 11.4 beta 2 they added a button in Navigation Bar that indicates hidden master view.

提交回复
热议问题