Я пытаюсь использовать NavigationSplitView для создания структуры из двух столбцов, где правый столбец зависит от левого столбца, а левый столбец немного больше правого. Думаю, мне бы хотелось, чтобы две колонки сложились на телефоне. В качестве простого примера я использую одно и то же представление в обоих столбцах.
Независимо от того, что я пытаюсь сделать, я не могу добиться того, чтобы левый столбец был шире примерно 25% iPad в альбомной ориентации (или около 35% в портретной ориентации). Мне нужно, чтобы левый столбец занимал около 60% ширины.
Я пытался:
Представление статично, поэтому мне нужен .navigationSplitViewStyle(.balanced) и нет панели инструментов.
Я просто использую неправильную вещь, должен ли я использовать что-то вместо NavigationSplitView для просмотра двух столбцов на iPad в альбомной ориентации?
struct SuggestionContentView : View {
@State var isPresented:Bool = false
@State var items = [
"crown",
"cereal",
"color",
"cookie",
"can",
"cat",
]
var nullAction: (() -> Void)?
var body: some View {
List {
ForEach(items, id:\.self) { item in
Button {
isPresented = true
} label: {
Text(item)
}
}
}
.navigationBarBackButtonHidden(true)
}
}
Вот макет с тремя столбцами и боковой панелью нулевой ширины:
struct ClickAndSuggestionLayout: View {
@State private var columnVisibility =
NavigationSplitViewVisibility.doubleColumn
var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
EmptyView().navigationSplitViewColumnWidth(0)
} content: {
SuggestionContentView()
.toolbar(.hidden, for: .navigationBar)
.navigationSplitViewColumnWidth( 800)
} detail: {
SuggestionContentView()
.toolbar(.hidden, for: .navigationBar)
.navigationSplitViewColumnWidth(400)
}
.navigationSplitViewStyle(.balanced)
}
}
Вот макет с двумя столбцами для онлайн-примеров:
struct RadialAndSuggestionLayout: View {
@State private var columnVisibility =
NavigationSplitViewVisibility.doubleColumn
var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
SuggestionContentView()
.toolbar(.hidden, for: .navigationBar)
.navigationSplitViewColumnWidth( 800)
} detail: {
SuggestionContentView()
.toolbar(.hidden, for: .navigationBar)
.navigationSplitViewColumnWidth(400)
}
.navigationSplitViewStyle(.balanced)
}
}
В обоих случаях я получаю такие превью:





Из документации ,
Только некоторые платформы позволяют изменять размер столбцов. Если вы укажете ширину, которую текущая среда представления не поддерживает, SwiftUI может использовать другую ширину для вашего столбца.
navigationSplitViewColumnWidth — это всего лишь «предложение». SwiftUI имеет полное право не изменять размер столбца до желаемой ширины. Судя по моим экспериментам, кажется, что navigationSplitViewColumnWidth вообще не устанавливает maximumPrimaryColumnWidth базового UISplitViewController, даже если вы передаете аргумент max:.
Чтобы это работало, вам нужно будет использовать API UISplitViewController. В частности, вам нужно установить preferredPrimaryColumnWidth или preferredPrimaryColumnWidthFraction на желаемую ширину, а maximumPrimaryColumnWidth на достаточно большое значение.
Например, вот как это можно сделать с помощью SwiftUI-Introspect.
NavigationSplitView {
Color.blue
} detail: {
Color.yellow
}
.introspect(.navigationSplitView, on: .iOS(.v16, .v17)) { splitView in
splitView.maximumPrimaryColumnWidth = .infinity
splitView.preferredPrimaryColumnWidthFraction = 2 / 3
}
Однако обертывание UISplitViewController в UIViewControllerRepresentable — более перспективное решение. Вот простой пример:
struct UIKitSplitView<Primary, Secondary, Selection>: UIViewControllerRepresentable
where Primary : View, Secondary : View, Selection: Hashable {
typealias UIViewControllerType = UISplitViewController
let primary: Primary
let secondary: Secondary
let selection: Selection?
init(selection: Selection?, @ViewBuilder primary: @escaping () -> Primary, @ViewBuilder secondary: @escaping () -> Secondary) {
self.primary = primary()
self.secondary = secondary()
self.selection = selection
}
func makeUIViewController(context: Context) -> UISplitViewController {
let splitViewController = UISplitViewController(style: .doubleColumn)
splitViewController.preferredPrimaryColumnWidth = 800
splitViewController.maximumPrimaryColumnWidth = .infinity
splitViewController.delegate = context.coordinator
let primaryHostingViewController = UIHostingController(rootView: primary)
let secondaryHostingViewController = UIHostingController(rootView: secondary)
splitViewController.setViewController(primaryHostingViewController, for: .primary)
splitViewController.setViewController(secondaryHostingViewController, for: .secondary)
return splitViewController
}
func updateUIViewController(_ uiViewController: UISplitViewController, context: Context) {
(uiViewController.viewController(for: .primary) as! UIHostingController<Primary>).rootView = primary
(uiViewController.viewController(for: .secondary) as! UIHostingController<Secondary>).rootView = secondary
if let selection {
uiViewController.show(.secondary)
}
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator: NSObject, UISplitViewControllerDelegate {
func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
return .primary
}
}
}
Пример использования:
struct ContentView: View
{
@State var selection: Int?
var body: some View {
// the "selection" parameter here acts as a trigger for showing the detail view
// when "selection" is not nil, the detail view is shown
UIKitSplitView(selection: selection) {
List([1,2,3,4], id: \.self, selection: $selection) {
Text("Row \($0)")
}
} secondary: {
if let selection {
Text("Details for \(selection)")
}
}
.ignoresSafeArea()
}
}
Благодарю за ответ, сейчас я просто использую HStack, так как у меня еще нет специального дизайна для телефонов.