Mac开发基础21-NSSplitView

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 创建可调整大小的复杂布局。在实际应用中,合理使用这些技巧可以显著提升用户界面的灵活性和用户体验。

posted @ 2024-08-06 17:55  Mr.陳  阅读(13)  评论(0编辑  收藏  举报