有什么方法可以禁用 SidebarListStyle NavigationViews 的可折叠性吗?
编辑:此方法截至 2022 年末仍然有效,并且从未停止在任何版本的 macOS 上工作(直到最新的 Ventura 13.1)。不知道为什么这里有答案提出其他建议。如果 Introspection 库更改了其 API,您可能需要相应地更新您的调用,但解决方案的要点是相同的。
使用这个 SwiftUI Introspection 库: https://github.com/siteline/SwiftUI-Introspect
我们可以通过扩展它们的功能来反思底层
NSSplitView
:
public func introspectSplitView(customize: @escaping (NSSplitView) -> ()) -> some View {
return introspect(selector: TargetViewSelector.ancestorOrSibling, customize: customize)
}
然后在
View
上创建通用扩展:
public extension View {
func preventSidebarCollapse() -> some View {
return introspectSplitView { splitView in
(splitView.delegate as? NSSplitViewController)?.splitViewItems.first?.canCollapse = false
}
}
}
可以在我们的侧边栏上使用:
var body: some View {
(...)
MySidebar()
.preventSidebarCollapse()
}
Oskar 提到的内省库不适用于 MacOS。
受此启发,我找到了适用于 MacOS 的解决方案。
解决方案背后的合理性是使用一种微妙的方式来找出
NavigationView
的父视图,即当前窗口中的 NSSplitViewController
。
以下代码在 XCode 13.2 和 macOS 12.1 上进行了测试。
var body: some View {
Text("Replace with your sidebar view")
.onAppear {
guard let nsSplitView = findNSSplitVIew(view: NSApp.windows.first?.contentView), let controller = nsSplitView.delegate as? NSSplitViewController else {
return
}
controller.splitViewItems.first?.canCollapse = false
// set the width of your side bar here.
controller.splitViewItems.first?.minimumThickness = 150
controller.splitViewItems.first?.maximumThickness = 150
}
}
private func findNSSplitVIew(view: NSView?) -> NSSplitView? {
var queue = [NSView]()
if let root = view {
queue.append(root)
}
while !queue.isEmpty {
let current = queue.removeFirst()
if current is NSSplitView {
return current as? NSSplitView
}
for subview in current.subviews {
queue.append(subview)
}
}
return nil
}
虽然 Oskar 在 Introspect 库中使用的方法不再有效,但我确实找到了另一种使用 Introspect 防止侧边栏折叠的方法。首先,你需要对View进行扩展:
extension View {
public func introspectSplitView(customize: @escaping (NSSplitView) -> ()) -> some View {
return inject(AppKitIntrospectionView(
selector: { introspectionView in
guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
return nil
}
return Introspect.findAncestorOrAncestorChild(ofType: NSSplitView.self, from: viewHost)
},
customize: customize
))
}
}
然后执行以下操作:
NavigationView {
SidebarView()
.introspectSplitView { controller in
(controller.delegate as? NSSplitViewController)?.splitViewItems.first?.canCollapse = false
}
Text("Main View")
}
话虽如此,我们不知道这实际上能持续多久。 Apple 可能会更改 NavigationView 的工作方式,并且此方法将来可能会停止工作。