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

*爱你&永不变心* 提交于 2019-12-20 12:26:32

问题


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 : View {
    var people = ["Angela", "Juan", "Yeji"]

    var body: some View {
        NavigationView {
            List {
                ForEach(people, id: \.self) { person in
                    NavigationLink(destination: Text("Hello!")) {
                        Text(person)
                    }
                }
            }
            Text("🤪")
        }
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

I get a splitView if an iPad simulator is in landscape, and the first detail screen is the emoji. But if people tap on a name, the detail view is "Hello!"

All that is great.

However, if I run the iPad in portrait, the user is greeted by the emoji, and then there is no indication that there is a list. You have to swipe from left to right to make the list appear from the side.

Does anyone know of a way to get even a navigation bar to appear that would let the user tap to see the list of items on the left? So that it's not a screen with the emoji only?

I would hate to leave a note that says "Swipe in from the left to see the list of files/people/whatever"

I remember UISplitViewController had a collapsed property that could be set. Is there anything like that here?


回答1:


In Xcode 11 beta 3, Apple has added .navigationViewStyle(style:) to NavigationView.

Updated for Xcode 11 Beta 5.
create MasterView() & DetailsView().

struct MyMasterView: View {

    var people = ["Angela", "Juan", "Yeji"]

    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)
    }
}

inside my ContentView :

var body: some View {

        NavigationView {

            MyMasterView()

            DetailsView()

        }.navigationViewStyle(DoubleColumnNavigationViewStyle())
         .padding()
    }

Output:




回答2:


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<Master: View, Detail: View>: 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.



来源:https://stackoverflow.com/questions/57211380/collapse-a-doublecolumn-navigationview-detail-in-swiftui-like-with-collapsed-on

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!