Outline View
xib-based
Drag NSOutlineView control to xib interface from control libraries, change control custom class to component class name of your exported.

Unlike other controls, there are several initialization and configuration methods in the controller's viewDidLoad method. The detailed code is as follows:
import Cocoa
class ViewController: NSViewController {
@IBOutlet weak var treeView: HDOutlineView!
var treeModel: HDTreeViewNode = HDTreeViewNode()
var fillColor: NSColor?
var fillGradient: NSGradient?
var arrowType: ExpandArrowType = .normal
var arrowFillColor: NSColor?
var rowFillColor: NSColor?
var rowGradient: NSGradient?
var rowHighlightColor: NSColor? = NSColor(hex: 0xDB4D52)
var rowHighlightGradient: NSGradient?
var rowStrokeColor: NSColor?
var rowStrokeWidth: CGFloat?
var rowTextColor: NSColor?
var rowTextFont: NSFont?
var rowTextAlignment: NSTextAlignment?
var rowTextLineBreakMode: NSLineBreakMode?
override func viewDidLoad() {
super.viewDidLoad()
//Style Config
treeViewStyleConfig()
//Set Delegate
treeViewDelegateConfig()
//Test Data Config
configViewData()
}
func treeViewStyleConfig() {
self.treeView.fillGradient = NSGradient.gradient(colorsStr: "rgba(130,0,233,1.0)-rgba(206,0,254,1.0)", locationsStr: "0.0-1.0")
self.arrowType = .triangle
self.arrowFillColor = NSColor(calibratedRed: 0.76, green: 0.24, blue: 0.92, alpha: 1.0)
self.rowFillColor = NSColor(calibratedRed: 0.20, green: 0.55, blue: 0.96, alpha: 1.0)
self.rowHighlightColor = NSColor(calibratedRed: 0.02, green: 0.25, blue: 0.76, alpha: 1.0)
self.rowStrokeWidth = 0.5
self.rowTextColor = NSColor(calibratedRed: 1.00, green: 1.00, blue: 1.00, alpha: 1.0)
self.rowTextLineBreakMode = .byTruncatingMiddle
updateTreeExpandArrow()
}
func treeViewDelegateConfig() {
self.treeView.delegate = self
self.treeView.dataSource = self
}
func updateTreeExpandArrow() {
let frame = NSRect(x: 0, y: 0, width: 13, height: 13)
if arrowType == .simple {
treeView.arrowButtonImage = NSImage.arrowButtonImage(in: frame, arrowSize: NSSize(width: 6, height: 8), fillColor: arrowFillColor, filled: false )
treeView.arrowButtonAltImage = NSImage.arrowButtonAltImage(in: frame, arrowSize: NSSize(width: 8, height: 6), fillColor: arrowFillColor, filled: false )
}
else if arrowType == .triangle {
treeView.arrowButtonImage = NSImage.arrowButtonImage(in: frame, arrowSize: NSSize(width: 8, height: 9), fillColor: arrowFillColor )
treeView.arrowButtonAltImage = NSImage.arrowButtonAltImage(in: frame, arrowSize: NSSize(width: 9, height: 8), fillColor: arrowFillColor)
}
else {
treeView.arrowButtonImage = nil
treeView.arrowButtonAltImage = nil
}
}
func configViewData() {
self.treeModel.childNodes.removeAll()
let rootNode = HDTreeViewNode()
rootNode.name = "Row1"
let rootNode2 = HDTreeViewNode()
rootNode2.name = "Row2"
self.treeModel.childNodes.append(rootNode)
self.treeModel.childNodes.append(rootNode2)
let level11Node = HDTreeViewNode()
level11Node.name = "Ecommerce"
let level12Node = HDTreeViewNode()
level12Node.name = "Game"
let level13Node = HDTreeViewNode()
level13Node.name = "Music"
rootNode.childNodes.append(level11Node)
rootNode.childNodes.append(level12Node)
rootNode.childNodes.append(level13Node)
rootNode2.childNodes.append(level13Node)
let level21Node = HDTreeViewNode()
level21Node.name = "Development"
let level22Node = HDTreeViewNode()
level22Node.name = "Operation"
level11Node.childNodes.append(level21Node)
level11Node.childNodes.append(level22Node)
self.treeView.reloadData()
}
}
extension ViewController: NSOutlineViewDelegate {
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
var view = outlineView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self)
if view == nil {
let containerView = NSTableCellView()
let imageView = NSImageView()
let textField = NSTextField.label()
textField.alignment = .left
containerView.addSubview(imageView)
containerView.addSubview(textField)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.width = 28
imageView.height = 28
imageView.left = 4
imageView.centerY = containerView.centerY
textField.translatesAutoresizingMaskIntoConstraints = false
textField.left = imageView.right + 4
textField.right = containerView.right - 4
textField.height = 17
textField.centerY = containerView.centerY
containerView.identifier = tableColumn?.identifier
view = containerView
}
guard let model = item as? HDTreeViewNode else {
return view
}
let subviews = view?.subviews
var imageView: NSImageView?
var textField: NSTextField?
if let imageViewTemp = subviews?[0] as? NSImageView {
imageView = imageViewTemp
}
if let textFieldTemp = subviews?[1] as? NSTextField {
textField = textFieldTemp
}
if let textFieldTemp = subviews?[0] as? NSTextField {
textField = textFieldTemp
}
if let imageViewTemp = subviews?[1] as? NSImageView {
imageView = imageViewTemp
}
textField?.stringValue = model.name!
if let textColor = rowTextColor {
textField?.textColor = textColor
}
if let textFont = rowTextFont {
textField?.font = textFont
}
if let alignment = rowTextAlignment {
textField?.alignment = alignment
}
if let lineBreakMode = rowTextLineBreakMode {
textField?.lineBreakMode = lineBreakMode
}
if model.childNodes.count <= 0 {
imageView?.image = NSImage(named: NSImage.listViewTemplateName)
}
else {
imageView?.image = NSImage(named: NSImage.folderName)
}
return view
}
func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
return 40
}
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
let identifier = NSUserInterfaceItemIdentifier(rawValue: "SelectionRowView")
var rowView = outlineView.makeView(withIdentifier:identifier, owner: nil) as? HDOutlineRowView
if rowView == nil {
rowView = HDOutlineRowView()
rowView?.identifier = identifier
}
rowView?.fillColor = self.rowFillColor
rowView?.gradient = self.rowGradient
rowView?.highlightColor = self.rowHighlightColor
rowView?.highlightGradient = self.rowHighlightGradient
rowView?.strokeColor = self.rowStrokeColor
rowView?.strokeWidth = self.rowStrokeWidth
return rowView
}
}
extension ViewController: NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
let rootNode:HDTreeViewNode
if item != nil {
rootNode = item as! HDTreeViewNode
}
else {
rootNode = self.treeModel
}
return rootNode.childNodes.count
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
let rootNode:HDTreeViewNode
if item != nil {
rootNode = item as! HDTreeViewNode
}
else {
rootNode = self.treeModel
}
return rootNode.childNodes[index]
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
let rootNode:HDTreeViewNode = item as! HDTreeViewNode
return rootNode.childNodes.count > 0
}
}
code-based
Declare lazy load variable treeView and scrollView in the controller class , add scrollView to the view. implement several initialization and configuration methods in the controller's viewDidLoad method. The detailed code is as follows:
import Cocoa
class ViewController: NSViewController {
var treeModel: HDTreeViewNode = HDTreeViewNode()
lazy var treeView: HDOutlineView = {
let treeView = HDOutlineView()
treeView.focusRingType = .none
treeView.delegate = self
treeView.dataSource = self
let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("name"))
column1.title = "name"
column1.width = 100
column1.maxWidth = 100
column1.minWidth = 100
treeView.addTableColumn(column1)
treeView.outlineTableColumn = column1
treeView.headerView = nil
return treeView
}()
lazy var scrollView: NSScrollView = {
let scrollView = NSScrollView()
scrollView.focusRingType = .none
scrollView.autohidesScrollers = true
scrollView.borderType = .noBorder
scrollView.documentView = self.treeView
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
var fillColor: NSColor?
var fillGradient: NSGradient?
var arrowType: ExpandArrowType = .normal
var arrowFillColor: NSColor?
var rowFillColor: NSColor?
var rowGradient: NSGradient?
var rowHighlightColor: NSColor? = NSColor(hex: 0xDB4D52)
var rowHighlightGradient: NSGradient?
var rowStrokeColor: NSColor?
var rowStrokeWidth: CGFloat?
var rowTextColor: NSColor?
var rowTextFont: NSFont?
var rowTextAlignment: NSTextAlignment?
var rowTextLineBreakMode: NSLineBreakMode?
override func viewDidLoad() {
super.viewDidLoad()
//Style Config
treeViewStyleConfig()
//Add Subview
addTabView()
//Set AutoLayout
setupAutolayout()
//Set Delegate
treeViewDelegateConfig()
//Test Data Config
configViewData()
}
func addTabView() {
self.view.addSubview(scrollView)
}
func setupAutolayout() {
scrollView.left = 10
scrollView.right = -10
scrollView.top = 10
scrollView.bottom = -10
}
func treeViewStyleConfig() {
self.treeView.fillColor = NSColor(calibratedRed: 1.00, green: 1.00, blue: 1.00, alpha: 1.0)
self.arrowType = .normal
self.arrowFillColor = NSColor(calibratedRed: 0.43, green: 0.43, blue: 0.43, alpha: 1.0)
self.rowFillColor = NSColor(calibratedRed: 1.00, green: 1.00, blue: 1.00, alpha: 1.0)
self.rowHighlightColor = NSColor(calibratedRed: 0.02, green: 0.26, blue: 0.76, alpha: 1.0)
self.rowStrokeWidth = 0.5
self.rowTextColor = NSColor(calibratedRed: 0.00, green: 0.00, blue: 0.00, alpha: 1.0)
self.rowTextLineBreakMode = .byTruncatingMiddle
updateTreeExpandArrow()
}
func treeViewDelegateConfig() {
self.treeView.focusRingType = .none
self.treeView.delegate = self
self.treeView.dataSource = self
}
func updateTreeExpandArrow() {
let frame = NSRect(x: 0, y: 0, width: 13, height: 13)
if arrowType == .simple {
treeView.arrowButtonImage = NSImage.arrowButtonImage(in: frame, arrowSize: NSSize(width: 6, height: 8), fillColor: arrowFillColor, filled: false )
treeView.arrowButtonAltImage = NSImage.arrowButtonAltImage(in: frame, arrowSize: NSSize(width: 8, height: 6), fillColor: arrowFillColor, filled: false )
}
else if arrowType == .triangle {
treeView.arrowButtonImage = NSImage.arrowButtonImage(in: frame, arrowSize: NSSize(width: 8, height: 9), fillColor: arrowFillColor )
treeView.arrowButtonAltImage = NSImage.arrowButtonAltImage(in: frame, arrowSize: NSSize(width: 9, height: 8), fillColor: arrowFillColor)
}
else {
treeView.arrowButtonImage = nil
treeView.arrowButtonAltImage = nil
}
}
func configViewData() {
self.treeModel.childNodes.removeAll()
let rootNode = HDTreeViewNode()
rootNode.name = "Row1"
let rootNode2 = HDTreeViewNode()
rootNode2.name = "Row2"
self.treeModel.childNodes.append(rootNode)
self.treeModel.childNodes.append(rootNode2)
let level11Node = HDTreeViewNode()
level11Node.name = "Ecommerce"
let level12Node = HDTreeViewNode()
level12Node.name = "Game"
let level13Node = HDTreeViewNode()
level13Node.name = "Music"
rootNode.childNodes.append(level11Node)
rootNode.childNodes.append(level12Node)
rootNode.childNodes.append(level13Node)
rootNode2.childNodes.append(level13Node)
let level21Node = HDTreeViewNode()
level21Node.name = "Development"
let level22Node = HDTreeViewNode()
level22Node.name = "Operation"
level11Node.childNodes.append(level21Node)
level11Node.childNodes.append(level22Node)
self.treeView.reloadData()
}
}
extension ViewController: NSOutlineViewDelegate {
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
// let identifier = NSUserInterfaceItemIdentifier(rawValue: (tableColumn?.identifier)!)
var view = outlineView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self)
if view == nil {
let containerView = NSTableCellView()
let imageView = NSImageView()
let textField = NSTextField.label()
textField.alignment = .left
containerView.addSubview(imageView)
containerView.addSubview(textField)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.width = 28
imageView.height = 28
imageView.left = 4
imageView.centerY = containerView.centerY
textField.translatesAutoresizingMaskIntoConstraints = false
textField.left = imageView.right + 4
textField.right = containerView.right - 4
textField.height = 17
textField.centerY = containerView.centerY
containerView.identifier = tableColumn?.identifier
view = containerView
}
guard let model = item as? HDTreeViewNode else {
return view
}
let subviews = view?.subviews
var imageView: NSImageView?
var textField: NSTextField?
if let imageViewTemp = subviews?[0] as? NSImageView {
imageView = imageViewTemp
}
if let textFieldTemp = subviews?[1] as? NSTextField {
textField = textFieldTemp
}
if let textFieldTemp = subviews?[0] as? NSTextField {
textField = textFieldTemp
}
if let imageViewTemp = subviews?[1] as? NSImageView {
imageView = imageViewTemp
}
textField?.stringValue = model.name!
if let textColor = rowTextColor {
textField?.textColor = textColor
}
if let textFont = rowTextFont {
textField?.font = textFont
}
if let alignment = rowTextAlignment {
textField?.alignment = alignment
}
if let lineBreakMode = rowTextLineBreakMode {
textField?.lineBreakMode = lineBreakMode
}
if model.childNodes.count <= 0 {
imageView?.image = NSImage(named: NSImage.listViewTemplateName)
}
else {
imageView?.image = NSImage(named: NSImage.folderName)
}
return view
}
func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
return 40
}
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
let identifier = NSUserInterfaceItemIdentifier(rawValue: "SelectionRowView")
var rowView = outlineView.makeView(withIdentifier:identifier, owner: nil) as? HDOutlineRowView
if rowView == nil {
rowView = HDOutlineRowView()
rowView?.identifier = identifier
}
rowView?.fillColor = self.rowFillColor
rowView?.gradient = self.rowGradient
rowView?.highlightColor = self.rowHighlightColor
rowView?.highlightGradient = self.rowHighlightGradient
rowView?.strokeColor = self.rowStrokeColor
rowView?.strokeWidth = self.rowStrokeWidth
return rowView
}
func outlineView(_ outlineView: NSOutlineView, didDrag tableColumn: NSTableColumn) {
Swift.print("didDrag outlineView")
}
}
extension ViewController: NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
let rootNode:HDTreeViewNode
if item != nil {
rootNode = item as! HDTreeViewNode
}
else {
rootNode = self.treeModel
}
return rootNode.childNodes.count
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
let rootNode:HDTreeViewNode
if item != nil {
rootNode = item as! HDTreeViewNode
}
else {
rootNode = self.treeModel
}
return rootNode.childNodes[index]
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
let rootNode:HDTreeViewNode = item as! HDTreeViewNode
return rootNode.childNodes.count > 0
}
}