NSSplitView
是 macOS 应用中的一个重要控件,允许用户调整窗口中的各个子视图大小。它通常用于创建可调整大小的面板布局,例如侧边栏和主内容区域。在本指南中,我们将详细介绍 NSSplitView
的常见 API 和基础技巧,并深入探讨相关知识。
基本使用
创建和初始化
Objective-C
#import <Cocoa/Cocoa.h>
// 创建一个 NSSplitView 实例
NSSplitView *splitView = [[NSSplitView alloc] initWithFrame:NSMakeRect(0, 0, 600, 400)];
// 设置 SplitView 为垂直分隔
[splitView setVertical:YES];
// 添加分配两边的子视图
NSView *leftView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 300, 400)];
[leftView setWantsLayer:YES]; // 启用 layer 支持(用于视图的主要特性设置)
[leftView.layer setBackgroundColor:[NSColor lightGrayColor].CGColor]; // 设置背景颜色
NSView *rightView = [[NSView alloc] initWithFrame:NSMakeRect(300, 0, 300, 400)];
[rightView setWantsLayer:YES]; // 启用 layer 支持
[rightView.layer setBackgroundColor:[NSColor blueColor].CGColor]; // 设置背景颜色
// 将子视图添加到 SplitView
[splitView addSubview:leftView];
[splitView addSubview:rightView];
Swift
import Cocoa
// 创建一个 NSSplitView 实例
let splitView = NSSplitView(frame: NSRect(x: 0, y: 0, width: 600, height: 400))
// 设置 SplitView 为垂直分隔
splitView.isVertical = true
// 添加分配两边的子视图
let leftView = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 400))
leftView.wantsLayer = true // 启用 layer 支持(用于视图的主要特性设置)
leftView.layer?.backgroundColor = NSColor.lightGray.cgColor // 设置背景颜色
let rightView = NSView(frame: NSRect(x: 300, y: 0, width: 300, height: 400))
rightView.wantsLayer = true // 启用 layer 支持
rightView.layer?.backgroundColor = NSColor.blue.cgColor // 设置背景颜色
// 将子视图添加到 SplitView
splitView.addSubview(leftView)
splitView.addSubview(rightView)
数据源和委托
使用 NSSplitView
时通常需要设置代理以处理各种事件。
Objective-C
// 设置代理
[splitView setDelegate:self];
// 实现代理方法,控制分隔条的位置和行为
- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
return proposedMinimumPosition + 50; // 最小分隔条位置
}
- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
return proposedMaximumPosition - 50; // 最大分隔条位置
}
- (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)view {
return YES; // 控制是否允许调整子视图大小
}
Swift
// 设置代理
splitView.delegate = self
// 实现代理方法,控制分隔条的位置和行为
func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return proposedMinimumPosition + 50 // 最小分隔条位置
}
func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return proposedMaximumPosition - 50 // 最大分隔条位置
}
func splitView(_ splitView: NSSplitView, shouldAdjustSizeOfSubview view: NSView) -> Bool {
return true // 控制是否允许调整子视图大小
}
使用自动布局
NSSplitView
支持自动布局,这使得调整窗口大小时能更好地适应变化。
Objective-C
// 使用自动布局进行初始化
NSView *leftView = [[NSView alloc] init];
NSView *rightView = [[NSView alloc] init];
[leftView setTranslatesAutoresizingMaskIntoConstraints:NO]; // 禁用自动转换约束
[rightView setTranslatesAutoresizingMaskIntoConstraints:NO];
[splitView addSubview:leftView];
[splitView addSubview:rightView];
// 添加约束
[NSLayoutConstraint activateConstraints:@[
[leftView.leadingAnchor constraintEqualToAnchor:splitView.leadingAnchor],
[leftView.topAnchor constraintEqualToAnchor:splitView.topAnchor],
[leftView.bottomAnchor constraintEqualToAnchor:splitView.bottomAnchor],
[leftView.widthAnchor constraintEqualToAnchor:splitView.widthAnchor multiplier:0.5],
[rightView.trailingAnchor constraintEqualToAnchor:splitView.trailingAnchor],
[rightView.topAnchor constraintEqualToAnchor:splitView.topAnchor],
[rightView.bottomAnchor constraintEqualToAnchor:splitView.bottomAnchor],
[rightView.widthAnchor constraintEqualToAnchor:splitView.widthAnchor multiplier:0.5],
]];
Swift
// 使用自动布局进行初始化
let leftView = NSView()
let rightView = NSView()
leftView.translatesAutoresizingMaskIntoConstraints = false // 禁用自动转换约束
rightView.translatesAutoresizingMaskIntoConstraints = false
splitView.addSubview(leftView)
splitView.addSubview(rightView)
// 添加约束
NSLayoutConstraint.activate([
leftView.leadingAnchor.constraint(equalTo: splitView.leadingAnchor),
leftView.topAnchor.constraint(equalTo: splitView.topAnchor),
leftView.bottomAnchor.constraint(equalTo: splitView.bottomAnchor),
leftView.widthAnchor.constraint(equalTo: splitView.widthAnchor, multiplier: 0.5),
rightView.trailingAnchor.constraint(equalTo: splitView.trailingAnchor),
rightView.topAnchor.constraint(equalTo: splitView.topAnchor),
rightView.bottomAnchor.constraint(equalTo: splitView.bottomAnchor),
rightView.widthAnchor.constraint(equalTo: splitView.widthAnchor, multiplier: 0.5),
])
高级用法
动态增减子视图
Objective-C
添加子视图
NSView *newView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 300, 400)];
[newView setWantsLayer:YES];
[newView.layer setBackgroundColor:[NSColor greenColor].CGColor];
// 动态添加到 SplitView
[splitView addSubview:newView positioned:NSWindowAbove relativeTo:nil];
[splitView adjustSubviews]; // 调整子视图
移除子视图
NSView *viewToRemove = [splitView.subviews lastObject];
[splitView removeArrangedSubview:viewToRemove];
[viewToRemove removeFromSuperview];
[splitView adjustSubviews]; // 调整子视图
Swift
添加子视图
let newView = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 400))
newView.wantsLayer = true
newView.layer?.backgroundColor = NSColor.green.cgColor
// 动态添加到 SplitView
splitView.addSubview(newView, positioned: .above, relativeTo: nil)
splitView.adjustSubviews() // 调整子视图
移除子视图
if let viewToRemove = splitView.subviews.last {
splitView.removeArrangedSubview(viewToRemove)
viewToRemove.removeFromSuperview()
splitView.adjustSubviews() // 调整子视图
}
自定义分隔条
Objective-C
创建自定义分隔条视图:
@interface CustomDividerView : NSView
@end
@implementation CustomDividerView
- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
self.wantsLayer = YES;
self.layer.backgroundColor = [NSColor darkGrayColor].CGColor;
}
return self;
}
@end
在 NSSplitView
代理方法中使用自定义分隔条:
- (NSView *)splitView:(NSSplitView *)splitView additionalEffectiveRectOfDividerAtIndex:(NSInteger)dividerIndex {
NSView *dividerView = [[CustomDividerView alloc] initWithFrame:NSMakeRect(0, 0, splitView.dividerThickness, 100)];
[splitView addSubview:dividerView];
return dividerView;
}
Swift
创建自定义分隔条视图:
class CustomDividerView: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.darkGray.cgColor
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
在 NSSplitView
代理方法中使用自定义分隔条:
func splitView(_ splitView: NSSplitView, additionalEffectiveRectOfDividerAt dividerIndex: Int) -> NSRect {
let dividerView = CustomDividerView(frame: NSRect(x: 0, y: 0, width: splitView.dividerThickness, height: 100))
splitView.addSubview(dividerView)
return dividerView.frame
}
用于实际应用
示例代码
Objective-C
#import <Cocoa/Cocoa.h>
#import "CustomDividerView.h"
@interface AppDelegate : NSObject <NSApplicationDelegate, NSSplitViewDelegate>
@property (strong) NSWindow *window;
@property (strong) NSSplitView *splitView;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
styleMask:(NSWindowStyleMaskTitled |
NSWindowStyleMaskClosable |
NSWindowStyleMaskResizable)
backing:NSBackingStoreBuffered
defer:NO];
[_window setTitle:@"NSSplitView Example"];
_splitView = [[NSSplitView alloc] initWithFrame:_window.contentView.bounds];
[_splitView setVertical:YES];
[_splitView setDelegate:self];
NSView *leftView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 300, 600)];
[leftView setWantsLayer:YES];
[leftView.layer setBackgroundColor:[NSColor lightGrayColor].CGColor];
NSView *rightView = [[NSView alloc] initWithFrame:NSMakeRect(300, 0, 500, 600)];
[rightView setWantsLayer:YES];
[rightView.layer setBackgroundColor:[NSColor blueColor].CGColor];
[_splitView addSubview:leftView];
[_splitView addSubview:rightView];
[_window.contentView addSubview:_splitView];
[_window makeKeyAndOrderFront:nil];
}
// NSSplitViewDelegate 方法实现
- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex {
return proposedMinimumPosition + 100;
}
- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex {
return proposedMaximumPosition - 100;
}
- (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)view {
return YES;
}
- (NSView *)splitView:(NSSplitView *)splitView additionalEffectiveRectOfDividerAtIndex:(NSInteger)dividerIndex {
NSView *dividerView = [[CustomDividerView alloc] initWithFrame:NSMakeRect(0, 0, splitView.dividerThickness, 600)];
[splitView addSubview:dividerView];
return dividerView;
}
@end
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}
Swift
import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate, NSSplitViewDelegate {
var window: NSWindow!
var splitView: NSSplitView!
func applicationDidFinishLaunching(_ aNotification: Notification) {
window = NSWindow(contentRect: NSMakeRect(0, 0, 800, 600),
styleMask: [.titled, .closable, .resizable],
backing: .buffered,
defer: false)
window.title = "NSSplitView Example"
splitView = NSSplitView(frame: window.contentView!.bounds)
splitView.isVertical = true
splitView.delegate = self
let leftView = NSView(frame: NSMakeRect(0, 0, 300, 600))
leftView.wantsLayer = true
leftView.layer?.backgroundColor = NSColor.lightGray.cgColor
let rightView = NSView(frame: NSMakeRect(300, 0, 500, 600))
rightView.wantsLayer = true
rightView.layer?.backgroundColor = NSColor.blue.cgColor
splitView.addSubview(leftView)
splitView.addSubview(rightView)
window.contentView?.addSubview(splitView)
window.makeKeyAndOrderFront(nil)
}
// NSSplitViewDelegate 方法实现
func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return proposedMinimumPosition + 100
}
func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return proposedMaximumPosition - 100
}
func splitView(_ splitView: NSSplitView, shouldAdjustSizeOfSubview view: NSView) -> Bool {
return true
}
func splitView(_ splitView: NSSplitView, additionalEffectiveRectOfDividerAt dividerIndex: Int) -> NSRect {
let dividerView = CustomDividerView(frame: NSRect(x: 0, y: 0, width: splitView.dividerThickness, height: 600))
splitView.addSubview(dividerView)
return dividerView.frame
}
}
class CustomDividerView: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
wantsLayer = true
layer?.backgroundColor = NSColor.darkGray.cgColor
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
总结
通过了解 NSSplitView
的基本使用、委托方法、动态增减子视图、自定义分隔条等高级用法,你将能够更有效地使用 NSSplitView
创建可调整大小的复杂布局。在实际应用中,合理使用这些技巧可以显著提升用户界面的灵活性和用户体验。