Last active
May 21, 2018 20:27
-
-
Save raphaeltraviss/3d8755b85d832bf3a69e18fecdddd76c to your computer and use it in GitHub Desktop.
Generic NSOutlineView delegate in Swift. Allows the delegate to be destroyed and re-created cleanly on every state change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AppKit | |
// This class will manufacture a delegate object for use with NSOutlineView, | |
// given the following: | |
// - A setup function, that will take an NSTableViewCell or subclass of the | |
// given type, and fill in its views. | |
// - A cell identifier from the storyboard, associated to the outline view. | |
// - A function that is called when a selection is made... | |
// Since the selection handler doesn't actually provide the selected node, | |
// you need to seal a reference to your outline view inside your Selection | |
// Action closure. Yuck. Make sure to use a capture list to prevent memory cycles. | |
final class OutlineDelegate<CellType, ModelType> : NSObject, NSOutlineViewDelegate { | |
typealias CellSetup = (CellType, ModelType) -> NSView? | |
typealias SelectionAction = () -> Void | |
typealias MoveAction = () -> Void | |
private let cell_identifier: String | |
private let configure_cell: CellSetup | |
private let selectionAction: SelectionAction | |
init(cell_identifier: String, | |
configure_cell: @escaping CellSetup, | |
selection_action: @escaping SelectionAction) | |
{ | |
self.cell_identifier = cell_identifier | |
self.configure_cell = configure_cell | |
self.selectionAction = selection_action | |
} | |
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { | |
let view = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cell_identifier), owner: self) as! CellType | |
return configure_cell(view, item as! ModelType) | |
} | |
func outlineViewSelectionDidChange(_ notification: Notification) { | |
selectionAction() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Code preceeds | |
This is just an example of how you might use such a delegate | |
*/ | |
// Temporarily disable state updates, to prevent an state management cycle | |
// from outlineView.selectRowIndexes(). | |
func new_state(state) { | |
var outline_state_updates_are_enabled = false | |
self.stored_delegate = OutlineDelegate<NSTableCellView, MyOutlineNodeClass>( | |
cell_identifier: "MyOutlineNodeCell", | |
configure_cell: { cell, node in | |
cell.textField?.stringValue = "\(node)" | |
return cell | |
}, | |
// @TODO: do we really need to capture a reference to the outline view, or just its data? | |
selection_action: { [weak outline_view = self.outlineView] () -> Void in | |
guard let outline = outline_view else { return } | |
guard let selected_node = outline.item(atRow: outline.selectedRow) as? MyOutlineNodeClass else { return } | |
guard outline_state_updates_are_enabled else { return } | |
let index = selected_node.state?.stack_index | |
GLOBAL.uiActionStore.dispatch(StateMutation.DoSomethingAtIndex(index)) | |
} | |
) | |
zoneGraphView.delegate = self.stored_delegate | |
} | |
/* More code follows */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Instead of capturing an entire reference to the NSOutlineView, it may be possible to only capture a reference to its
makeView
method. You still should use a capture list, however. Also, remember that delegates and datasources in AppKit are weak references, so you will need to save the re-created delegate/datasource instances to a state variable somewhere, otherwise they will be garbage-collected!