NSOutlineView with usesAutomaticRowHeights causes log "WARNING: Application performed a reentrant operation in its NSTableView delegate."

86 views Asked by At

What does this warning mean? In my main app, whenever this warning is logged, the outline view begins to behave weirdly after that, overlapping rows and not responding to clicks anymore. When not using automatic row heights or group rows, the warning doesn't appear anymore.

An interesting thing is that even if there are no group rows, simply implementing the data source method

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

makes the warning appear.

Here is the full code:

import Cocoa

class ViewController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate {
    
    var outlineView: NSOutlineView!
    
    let data: Any = [[[[[[[0], [0, [0,0,0]]], [0, [0, [0,0]],0,0,0,0,0]]], [[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],1]]]]

    override func loadView() {
        outlineView = NSOutlineView(dataSource: self, delegate: self, columns: [
            NSTableColumn(identifier: "", title: "", width: 400),
            NSTableColumn(identifier: "", title: "", width: 100)
        ])
        let scrollView = NSScrollView(documentView: outlineView)
        view = NSView()
        view.addSubview(scrollView)
        NSLayoutConstraint.constrain(scrollView, toMarginsOf: view, edgeInsets: .zero)
        outlineView.reloadData()
        expandItem(data)
    }

    private func expandItem(_ item: Any) {
        if let item = item as? [Any] {
            outlineView.expandItem(item)
            for content in item {
                expandItem(content)
            }
        }
    }
    
    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
        return item is [Any]
    }
    
    func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool {
        return false
    }

    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
        return ((item ?? data) as! [Any]).count
    }
    
    func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
        return ((item ?? data) as! [Any])[index]
    }
    
    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
        let view = NSTableCellView()
        return view
    }

}


extension NSOutlineView {
    
    convenience init(dataSource: NSOutlineViewDataSource, delegate: NSOutlineViewDelegate, columns: [NSTableColumn]) {
        self.init()
        self.dataSource = dataSource
        self.delegate = delegate
        for column in columns {
            addTableColumn(column)
        }
        usesAutomaticRowHeights = true
    }
    
}

extension NSTableColumn {
    
    convenience init(identifier: String, title: String, width: Double, minWidth: Double = 10, maxWidth: Double = .infinity) {
        self.init(identifier: NSUserInterfaceItemIdentifier(identifier))
        self.title = title
        self.width = width
        self.minWidth = minWidth
        self.maxWidth = maxWidth
    }
    
}

extension NSLayoutConstraint {
    
    static func constrain(_ view: NSView, toMarginsOf superview: NSView, edgeInsets: NSEdgeInsets) {
        NSLayoutConstraint.activate([NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: superview, attribute: .top, multiplier: 1, constant: edgeInsets.top), NSLayoutConstraint(item: view, attribute: .left, relatedBy: .equal, toItem: superview, attribute: .left, multiplier: 1, constant: edgeInsets.left), NSLayoutConstraint(item: superview, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: edgeInsets.bottom), NSLayoutConstraint(item: superview, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1, constant: edgeInsets.right)])
    }
    
}

extension NSEdgeInsets {
    
    static var zero: NSEdgeInsets {
        return NSEdgeInsets(all: 0)
    }
    
    init(all: Double) {
        self.init(top: all, left: all, bottom: all, right: all)
    }
    
}

extension NSScrollView {
    
    convenience init(documentView: NSView) {
        self.init()
        self.documentView = documentView
        hasVerticalScroller = true
        translatesAutoresizingMaskIntoConstraints = false
    }
}
0

There are 0 answers