NSOutlineView 使用指南

2020-05-10  本文已影响0人  zhongxiaoyue

简介

NSOutlineView(大纲视图)继承着NSTableView,两者有许多相同的地方,同样能够展示列表数据,但是NSOutlineView能够方便的实现数据列表的折叠和展开,就像macOS上Finder中的目录和子目录一样.所以它对于显示层次结构数据非常有用。

NSOutlineView

使用

let tableColumn = NSTableColumn.init()

let outlineView = NSOutlineView.init()
outlineView.delegate = self;
outlineView.dataSource = self;
outlineView.headerView = nil
outlineView.addTableColumn(tableColumn)
outlineView.outlineTableColumn = tableColumn

let scrollView = NSScrollView.init(frame: NSMakeRect(0, 0, 500, 500))
scrollView.documentView = outlineView
view.addSubview(scrollView)
class TableCellView: NSTableCellView {
    
    let icon = NSImageView.init()
    let title = NSTextField.init()
    
    var model: TreeNodeModel? {
        didSet {
            title.stringValue = model?.name ?? ""
            if model?.childNodes.count ?? 0 > 0 {
                icon.image = NSImage.init(named: NSImage.Name.folder)
            }else{
                icon.image = NSImage.init(named: NSImage.Name.listViewTemplate)
            }
        }
    }
    
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        setupUI()
    }
    
    func setupUI() {
        
        // 添加图片
        icon.frame = NSRect.init(x: 5, y: 5, width: 30, height: 30)
        icon.image = NSImage.init(named: NSImage.Name(rawValue: "NSFolder"))
        addSubview(icon)
        
        // 添加标题
        title.frame = NSRect.init(x: 45, y: 10, width: 100, height: 20)
        title.font = NSFont.boldSystemFont(ofSize: 15)
        title.isBordered = false
        title.isEditable = false
        title.textColor = NSColor.black
        title.bezelStyle = .roundedBezel
        addSubview(title)
    }
    
    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // Drawing code here.
    }
}
class TreeNodeModel: NSObject {
    var name: String?
    lazy var childNodes: Array = {
        return [TreeNodeModel]()
    }()
}
var treeModel: TreeNodeModel = TreeNodeModel()

override func viewDidLoad() {
    super.viewDidLoad()
    configData()
}

func configData() {
    
    let rootNode = TreeNodeModel()
    rootNode.name = "网易"
    let rootNode2 = TreeNodeModel()
    rootNode2.name = "腾讯"
    
    self.treeModel.childNodes.append(rootNode)
    self.treeModel.childNodes.append(rootNode2)
    
    let level11Node = TreeNodeModel()
    level11Node.name = "电商"
    let level12Node = TreeNodeModel()
    level12Node.name = "游戏"
    let level13Node = TreeNodeModel()
    level13Node.name = "音乐"
    
    rootNode.childNodes.append(level11Node)
    rootNode.childNodes.append(level12Node)
    rootNode.childNodes.append(level13Node)
    rootNode2.childNodes.append(level13Node)
   
    let level21Node = TreeNodeModel()
    level21Node.name = "研发"
    let level22Node = TreeNodeModel()
    level22Node.name = "运营"
    
    level11Node.childNodes.append(level21Node)
    level11Node.childNodes.append(level22Node)
    self.treeView.reloadData()
}

返回节点数,当item为nil时,表示根节点
-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(nullable id)item;
返回每个节点的数据模型model,当item为nil时,表示根节点
-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(nullable id)item;
节点是否允许展开
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;

示例代码

extension ViewController: NSOutlineViewDataSource {
    
    // 返回节点数,当item为nil时,表示根节点
    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
        
        let rootNode:TreeNodeModel
        
        if item != nil {
            rootNode = item as! TreeNodeModel
        }else{
            rootNode =  self.treeModel
        }
        
        return rootNode.childNodes.count
    }
    
    // 返回每个节点的数据模型model,当item为nil时,表示根节点
    func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
        
        let rootNode:TreeNodeModel
        
        if item != nil {
            rootNode = item as! TreeNodeModel
        }
        else {
            rootNode =  self.treeModel
        }
        
        return rootNode.childNodes[index]
    }
    
    // 节点是否允许展开
    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
        
        let rootNode:TreeNodeModel = item as! TreeNodeModel
        
        return rootNode.childNodes.count > 0
    }
}

返回每个节点对应的视图,类似TableView中的Cell
-(nullable NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(nullable NSTableColumn *)tableColumn item:(id)item;
返回每个节点的行高
-(CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item;
节点被选中或取消
-(void)outlineViewSelectionDidChange:(NSNotification *)notification;(id)item;

示例代码

extension ViewController: NSOutlineViewDelegate {
    
    // 返回节点对应的视图
    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
        
        let identifire = NSUserInterfaceItemIdentifier(rawValue: "identifire")
        var view = outlineView.makeView(withIdentifier: identifire, owner: self) as? TableCellView
        if (view == nil) {
            view = TableCellView.init()
        }
        
        let model = item as! TreeNodeModel
        view?.model = model
        
        return view
    }
    
    // 行高
    func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
        return 40
    }
    
    // 节点选中或取消
    func outlineViewSelectionDidChange(_ notification: Notification) {
        
        let treeView = notification.object as! NSOutlineView
        // 获取选中节点模型
        let row = treeView.selectedRow
        let model = treeView.item(atRow: row)
        print(model)
    }
}

完成效果

常用方法

let addNode = TreeNodeModel()
addNode.name = nodeName
item?.childNodes.append(addNode)
outlineView.reloadData()
// 展开节点
open func expandItem(_ item: Any?)
// 展开节点,expandChildren是否展开节点内的所有子节点
open func expandItem(_ item: Any?, expandChildren: Bool)
// 收缩节点
open func collapseItem(_ item: Any?)
// 收缩节点,collapseChildren是否收缩节点内的所有子节点
open func collapseItem(_ item: Any?, collapseChildren: Bool)
// 展开所有节点
outlineView.expandItem(nil, expandChildren: true)

// 关闭第一行节点,及节点内的所有子节点
let model = oulineView.item(atRow: 1)
outlineView.collapseItem(model, collapseChildren: true)
open func selectRowIndexes(_ indexes: IndexSet, byExtendingSelection extend: Bool)
let indexSet: IndexSet = [1,3]
outlineView.selectRowIndexs(indexSet, byExtendingSelection: false)

自定义

override func makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner: Any?) -> NSView?{
    
    let view = super.makeView(withIdentifier: identifier, owner: owner)
    
    if (identifier == NSOutlineView.disclosureButtonIdentifier) {
        let button:NSButton = view as! NSButton
        // 自定义收缩图片
        button.image = NSImage(named: NSImage.Name(rawValue: "Plus"))!
        // 自定义展开图片
        button.alternateImage = NSImage(named: NSImage.Name(rawValue: "Minus"))!
        button.isBordered = false
        button.title = ""
        return button
    }
    return view
}
var nodeMenu: NSMenu?

override func menu(for event: NSEvent) -> NSMenu? {

    let pt = self.convert(event.locationInWindow, from: nil)
    let row = self.row(at: pt)
    if row >= 0 {
        // 返回自定义菜单
        return self.nodeMenu
    }
    return super.menu(for: event)!
}
上一篇下一篇

猜你喜欢

热点阅读