Skip to content
This repository was archived by the owner on Jun 9, 2022. It is now read-only.

Latest commit

 

History

History
115 lines (94 loc) · 3.42 KB

File metadata and controls

115 lines (94 loc) · 3.42 KB

AnyOutlineView

An NSOutlineView that actually works with Any, i.e. w/ structs and enums.

NSOutlineView only works w/ AnyObjects, because it uses identity to maintain its internal state. I.e. the API's Any parameters are incorrectly mapped to Swift as Any. They actually have to be AnyObjects for all but the most static outline view.

This subclass works around that by maintaining an object tree for the new AnyOutlineViewDataSource. To make this work, that datasource needs to be able to give the items an "identity".

This view doesn't come for free, it essentially has to mirror the whole data provided by the anyDataSource. It tries to do lookups lazily though.

Example

Model

enum Section: String, CaseIterable {
  
    case dropbox      = "Dropbox"
    case airdrop      = "AirDrop"
    case applications = "Applications"
    case desktop      = "Desktop"
    
    var items : [ String ] {
        switch self {
        case .dropbox: return [ "Public", "Documents", "Other" ]
        case .airdrop: return [ "MacPro2020", "iMac Blueberry" ]
        case .applications:
          return [ "Diagram.app", "FrameMaker.app", "ProjectBuilder.app" ]
        case .desktop: return [ "Kaffee", "Kuchen", "Krümel" ]
        }
    }
}

AnyOutlineViewDataSource

extension AppDelegate: AnyOutlineViewDataSource {
  
    func outlineView(_ outlineView: AnyOutlineView,
                     numberOfChildrenOfItem item: Any?) -> Int
    {
        switch item {
            case .none:                  return Section.allCases.count // root
            case let section as Section: return section.items.count
            default:                     return 0
        }
    }
    
    func outlineView(_ outlineView: AnyOutlineView,
                     child index: Int, ofItem item: Any?) -> Any
    {
        switch item {
            case .none:                  return Section.allCases[index]
            case let section as Section: return section.items[index]
            default: fatalError("unexpected item: \(item as Any)")
        }
    }
    
    func outlineView(_ outlineView: AnyOutlineView, isItemExpandable item: Any)
         -> Bool
    {
        return item is Section
    }
}

AnyOutlineViewDelegate

extension AppDelegate: AnyOutlineViewDelegate {
  
    enum CellID {
        static let header = NSUserInterfaceItemIdentifier(rawValue: "HeaderCell")
        static let data   = NSUserInterfaceItemIdentifier(rawValue: "DataCell")
    }
    
    func outlineView(_ outlineView: AnyOutlineView,
                     viewFor tableColumn: NSTableColumn?, item: Any) -> NSView?
    {
        let cell : NSTableCellView
        if let section = item as? Section {
            cell = outlineView.makeView(withIdentifier: CellID.header, owner: nil)
                   as! NSTableCellView
            cell.textField?.stringValue = section.rawValue
        }
        else {
            cell = outlineView.makeView(withIdentifier: CellID.data, owner: nil)
                   as! NSTableCellView
            cell.textField?.stringValue = String(describing: item)
        }
        return cell
    }
}

Who

Brought to you by The Always Right Institute and ZeeZide. We like feedback, GitHub stars, cool contract work, presumably any form of praise you can think of.