You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用Interface Builder,代码创建NSOutlineView无数据问题求助

NSOutlineView 空白问题的修复方案

核心问题分析与修复步骤

1. 根节点未持久化存储

viewDidLoad中创建的rootNode是局部变量,控制器的数据源方法无法访问到它。需要将其改为控制器的属性:

private var rootNode: Node! // 添加根节点属性

在viewDidLoad中初始化数据并刷新视图:

override func viewDidLoad() {
    super.viewDidLoad()
    rootNode = Node(name: "Root Node")
    let childNode1 = Node(name: "Child Node 1")
    let childNode2 = Node(name: "Child Node 2")
    rootNode.addChild(childNode1)
    rootNode.addChild(childNode2)
    outlineView.reloadData() // 数据初始化完成后再刷新
}

2. 数据源方法逻辑错误

当前numberOfChildrenOfItem方法中,当item为nil(代表根层级)时返回0,导致OutlineView认为没有根节点。修改数据源方法:

func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
    if let item = item as? Node {
        return item.children.count
    } else {
        // 根层级返回根节点数量
        return rootNode != nil ? 1 : 0
    }
}

func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
    if let item = item as? Node {
        return item.children[index]
    } else {
        // 返回根节点
        return rootNode!
    }
}

3. NSOutlineView 缺少列

NSOutlineView必须至少有一列才能显示内容,添加列并设置标识符:

// 创建outlineView后添加列
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "MainColumn"))
outlineView.addTableColumn(column)

4. 滚动视图约束问题

当前scrollview使用frame布局且未设置自身约束,同时outlineView的约束会与scrollview的documentView自动布局冲突。修改为约束布局:

// 创建scrollview时关闭自动布局转换
let scrollview = NSScrollView()
scrollview.translatesAutoresizingMaskIntoConstraints = false
scrollview.documentView = outlineView
view.addSubview(scrollview)

// 给scrollview添加约束固定在父视图中
NSLayoutConstraint.activate([
    scrollview.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    scrollview.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    scrollview.widthAnchor.constraint(equalToConstant: 800),
    scrollview.heightAnchor.constraint(equalToConstant: 600)
])

5. 代码语法错误

删除OutlineViewController类末尾多余的}。

修改后的完整代码

class OutlineViewController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate {
    // MARK: - Properties
    private var outlineView: NSOutlineView!
    private var rootNode: Node! // 根节点属性

    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        // 初始化数据
        rootNode = Node(name: "Root Node")
        let childNode1 = Node(name: "Child Node 1")
        let childNode2 = Node(name: "Child Node 2")
        rootNode.addChild(childNode1)
        rootNode.addChild(childNode2)
        // 数据初始化完成后刷新视图
        outlineView.reloadData()
    }

    override func loadView() {
        view = NSView()
        view.translatesAutoresizingMaskIntoConstraints = false

        // 创建大纲视图
        outlineView = NSOutlineView()
        outlineView.translatesAutoresizingMaskIntoConstraints = false
        outlineView.rowHeight = 50
        // 添加列(必须步骤)
        let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "MainColumn"))
        outlineView.addTableColumn(column)

        // 设置代理和数据源
        outlineView.delegate = self
        outlineView.dataSource = self

        // 创建滚动视图
        let scrollview = NSScrollView()
        scrollview.translatesAutoresizingMaskIntoConstraints = false
        scrollview.documentView = outlineView
        view.addSubview(scrollview)

        // 约束滚动视图
        NSLayoutConstraint.activate([
            scrollview.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            scrollview.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            scrollview.widthAnchor.constraint(equalToConstant: 800),
            scrollview.heightAnchor.constraint(equalToConstant: 600)
        ])
    }

    // MARK: - NSOutlineViewDataSource
    func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
        if let item = item as? Node {
            return item.children[index]
        } else {
            // 返回根节点
            return rootNode!
        }
    }

    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
        guard let item = item as? Node else { return false }
        return item.children.count > 0
    }

    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
        if let item = item as? Node {
            return item.children.count
        } else {
            // 根层级返回根节点数量
            return rootNode != nil ? 1 : 0
        }
    }

    // MARK: - NSOutlineViewDelegate
    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
        guard let node = item as? Node else { return nil }
        let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: TableCellView.identifier)
        
        guard let cell = outlineView.makeView(withIdentifier: cellIdentifier, owner: self) as? TableCellView else {
            let cell = TableCellView()
            cell.identifier = cellIdentifier
            return cell
        }
        
        // 根据节点内容设置单元格
        cell.profileImage.image = NSImage(named: node.name.contains("1") ? "boy" : "girl")
        cell.label.stringValue = node.name
        return cell
    }
}

class Node {
    let name: String
    var children: [Node] = []

    init(name: String) {
        self.name = name
    }

    func addChild(_ child: Node) {
        children.append(child)
    }
}

class TableCellView: NSTableCellView {
    static var identifier = "TableCell"
    
    var profileImage = NSImageView()
    var label = NSTextField(labelWithString: "")
    
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        addSubview(profileImage)
        addSubview(label)
        
        profileImage.wantsLayer = true
        profileImage.animates = true
        profileImage.layer?.cornerRadius = 45
        
        label.isEditable = false
        label.alignment = .center
        label.font = .boldSystemFont(ofSize: 20)
        
        // 关闭自动布局转换
        profileImage.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            profileImage.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 50),
            profileImage.widthAnchor.constraint(equalToConstant: 90),
            profileImage.topAnchor.constraint(equalTo: topAnchor, constant: 20),
            profileImage.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
            
            label.leadingAnchor.constraint(equalTo: profileImage.trailingAnchor, constant: 20),
            label.centerYAnchor.constraint(equalTo: centerYAnchor),
            label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20)
        ])
    }
}

内容的提问来源于stack exchange,提问作者RRSK01_11

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 02:22:34