NSSplitView 是 macOS AppKit 框架中的一个类,用于将窗口或视图划分为多个可调整大小的子视图区域,比如 Finder 左侧的目录树和右侧的文件列表,或者 Xcode 的侧边栏、代码编辑区和调试面板。

基本用法
1、创建NSSplictView:
let splitView = NSSplitView()
2、设置子视图
let splitView = NSSplitView()
splitView.isVertical = true // true = 左右分栏,false = 上下分栏
// 添加左视图
let leftView = NSView()
leftView.wantsLayer = true
leftView.layer?.backgroundColor = NSColor.red.cgColor
// 添加右视图
let rightView = NSView()
rightView.wantsLayer = true
rightView.layer?.backgroundColor = NSColor.blue.cgColor
splitView.addArrangedSubview(leftView)
splitView.addArrangedSubview(rightView)

水平或垂直划分区域,用户可以拖动中间的分隔条调整每个区域的大小。
常用属性和方法
1、isVertical:是否垂直分割(左右)。
splitView.isVertical = true // true = 左右分栏,false = 上下分栏
2、addArrangedSubview(_:):添加子视图。
splitView.addArrangedSubview(leftView)
3、setPosition(_:ofDividerAt:):设置某个分割条的位置。
DispatchQueue.main.async {
splitView.setPosition(100, ofDividerAt: 0)
splitView.adjustSubviews()
}
参数 100:表示第 0 个分割条左(或上)侧的视图宽度或高度
参数 ofDividerAt: 0:第 0 个分隔条(即第一个分栏)
注意,需要延迟设置分割条的位置,让 layout 先完成,否则无法限制分割条宽度或高度。

4、adjustSubviews():强制重新布局。
通常在初始化子视图、调用setPosition(…)后,改变视图大小或更新内容后调用,它会自动依据当前分割条位置重新布局所有 arrangedSubview。
5、dividerThickness:分隔条宽度,只读属性。
let thickness = splitView.dividerThickness
通常是 1 或 2 像素,可用于计算布局偏移或自定义分隔行为时参考。
6、delegate:实现拖动限制或自定义行为。
通过实现 NSSplitViewDelegate 来控制拖动行为,比如:
限制某一栏最小或最大尺寸。
防止某一栏被拖没(宽度为 0)。
只允许某一侧变化,另一侧保持固定大小。
class MySplitDelegate: NSObject, NSSplitViewDelegate {
func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMin: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return 150 // 限制左侧最小宽度 150pt
}
func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMax: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return proposedMax - 150 // 限制右侧最小宽度
}
}
使用方法:
let splitView = NSSplitView()
splitView.delegate = MySplitDelegate()
使用场景
1、在SwiftUI中实现NSSplitView
import SwiftUI
import AppKit
struct LefttView: View {
var body: some View {
VStack {
Text("左视图")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct RightView: View {
var body: some View {
VStack {
Text("右视图")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.red.opacity(0.3))
}
}
struct ContentView: View {
var body: some View {
VStack {
MySlider()
}
.frame(width: 400,height:300)
}
}
struct MySlider: NSViewRepresentable {
// @Binding var value: Double
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeNSView(context: Context) -> NSSplitView {
let splitView = NSSplitView()
splitView.isVertical = true // true = 左右分栏,false = 上下分栏
// 添加左视图
let leftView = NSHostingController(rootView: LefttView()).view
// 添加右视图
let rightView = NSHostingController(rootView: RightView()).view
splitView.addArrangedSubview(leftView)
splitView.addArrangedSubview(rightView)
DispatchQueue.main.async {
splitView.setPosition(100, ofDividerAt: 0)
splitView.adjustSubviews()
}
splitView.delegate = context.coordinator.delegate
return splitView
}
func updateNSView(_ nsView: NSSplitView, context: Context) { }
class Coordinator {
let delegate = MySplitDelegate()
}
}
class MySplitDelegate: NSObject, NSSplitViewDelegate {
func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMin: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return 150
}
func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMax: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return splitView.bounds.width - 150
}
}
总结
NSSplitView可以灵活划分界面、多区域,支持用户拖动调整大小。
可水平/垂直切割,潜逃使用,拖动行为需手动控制或代理实现。
当有多个NSViewController时,建议使用NSSplitViewController管理,可以自动管理子控制器的生命周期。