NSOutlineView 中托管 NSView 的层

作者:编程家 分类: objective 时间:2025-07-11

使用 NSOutlineView 来托管 NSView 的层是在 macOS 开发中常见的一种技术。NSOutlineView 是一种可以显示层级数据的视图控件,而 NSView 是 macOS 中用于绘制用户界面的基本元素。通过将 NSView 放置在 NSOutlineView 中的不同层级中,我们可以实现多级菜单、文件浏览器等功能。下面将介绍如何使用 NSOutlineView 托管 NSView 的层,并提供一个简单的案例代码。

首先,我们需要创建一个 NSOutlineView,并设置它的数据源和代理。数据源负责提供 NSOutlineView 所需的数据,而代理则处理 NSOutlineView 的交互事件和自定义绘制。

swift

class MyOutlineViewDataSource: NSObject, NSOutlineViewDataSource {

// 实现数据源方法

}

class MyOutlineViewDelegate: NSObject, NSOutlineViewDelegate {

// 实现代理方法

}

let outlineView = NSOutlineView()

outlineView.dataSource = MyOutlineViewDataSource()

outlineView.delegate = MyOutlineViewDelegate()

接下来,我们需要定义一个数据模型来表示 NSView 的层级结构。通常,我们可以使用树状结构来表示层级数据,每个节点代表一个 NSView。

swift

class Node {

var name: String

var subnodes: [Node]

init(name: String, subnodes: [Node] = []) {

self.name = name

self.subnodes = subnodes

}

}

然后,我们需要在数据源中实现必要的方法,以提供 NSOutlineView 所需的数据。在这个例子中,我们使用一个包含多个节点的树状结构作为数据源。

swift

class MyOutlineViewDataSource: NSObject, NSOutlineViewDataSource {

var rootNodes: [Node]

init(rootNodes: [Node]) {

self.rootNodes = rootNodes

}

// 返回根节点的数量

func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {

if item == nil {

return rootNodes.count

}

if let node = item as? Node {

return node.subnodes.count

}

return 0

}

// 返回指定索引的子节点

func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {

if item == nil {

return rootNodes[index]

}

if let node = item as? Node {

return node.subnodes[index]

}

fatalError("Invalid item")

}

// 判断节点是否为叶子节点

func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {

if let node = item as? Node {

return !node.subnodes.isEmpty

}

return false

}

// 返回节点对应的视图

func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {

// 返回自定义的 NSView,用于绘制节点的内容

}

}

最后,我们需要在代理中实现必要的方法,以处理 NSOutlineView 的交互事件和自定义绘制。

swift

class MyOutlineViewDelegate: NSObject, NSOutlineViewDelegate {

// 实现代理方法

}

案例代码:使用 NSOutlineView 托管 NSView 的层

下面是一个简单的例子,演示了如何使用 NSOutlineView 托管 NSView 的层。假设我们有一个文件系统的层级结构,我们可以使用 NSOutlineView 来显示文件夹和文件的层级关系。

swift

// 定义文件系统节点的数据模型

class FileSystemNode {

var name: String

var isFolder: Bool

var subnodes: [FileSystemNode]

init(name: String, isFolder: Bool, subnodes: [FileSystemNode] = []) {

self.name = name

self.isFolder = isFolder

self.subnodes = subnodes

}

}

// 数据源

class FileSystemOutlineDataSource: NSObject, NSOutlineViewDataSource {

var rootNodes: [FileSystemNode]

init(rootNodes: [FileSystemNode]) {

self.rootNodes = rootNodes

}

func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {

if item == nil {

return rootNodes.count

}

if let node = item as? FileSystemNode {

return node.subnodes.count

}

return 0

}

func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {

if item == nil {

return rootNodes[index]

}

if let node = item as? FileSystemNode {

return node.subnodes[index]

}

fatalError("Invalid item")

}

func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {

if let node = item as? FileSystemNode {

return node.isFolder && !node.subnodes.isEmpty

}

return false

}

func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {

let view = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("Cell"), owner: self) as? NSTableCellView

if let fileSystemNode = item as? FileSystemNode {

view?.textField?.stringValue = fileSystemNode.name

view?.imageView?.image = fileSystemNode.isFolder ? NSImage(named: NSImage.folderName) : NSImage(named: NSImage.genericDocumentName)

}

return view

}

}

// 代理

class FileSystemOutlineDelegate: NSObject, NSOutlineViewDelegate {

func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {

return 20.0

}

func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool {

return false

}

func outlineViewSelectionDidChange(_ notification: Notification) {

// 处理选中节点变化的逻辑

}

}

// 创建 NSOutlineView

let outlineView = NSOutlineView()

outlineView.dataSource = FileSystemOutlineDataSource(rootNodes: [

FileSystemNode(name: "Documents", isFolder: true, subnodes: [

FileSystemNode(name: "README.txt", isFolder: false),

FileSystemNode(name: "Images", isFolder: true, subnodes: [

FileSystemNode(name: "image1.jpg", isFolder: false),

FileSystemNode(name: "image2.jpg", isFolder: false)

])

]),

FileSystemNode(name: "Projects", isFolder: true, subnodes: [

FileSystemNode(name: "Project1", isFolder: true, subnodes: [

FileSystemNode(name: "File1.swift", isFolder: false),

FileSystemNode(name: "File2.swift", isFolder: false)

]),

FileSystemNode(name: "Project2", isFolder: true, subnodes: [

FileSystemNode(name: "File1.swift", isFolder: false),

FileSystemNode(name: "File2.swift", isFolder: false)

])

])

])

outlineView.delegate = FileSystemOutlineDelegate()

在这个案例中,我们创建了一个文件系统的层级结构,并使用 NSOutlineView 托管了这个层级结构。每个节点代表一个文件夹或文件,通过设置节点的 isFolder 属性来区分。我们还实现了数据源和代理的方法,以提供数据和自定义绘制。最后,我们创建了一个 NSOutlineView,并将数据源和代理设置为我们定义的类。

这就是使用 NSOutlineView 托管 NSView 的层的基本方法。通过这种方式,我们可以轻松地实现多级菜单、文件浏览器等功能。希望这个例子能对你在 macOS 开发中的工作有所帮助!