Skip to content

Instantly share code, notes, and snippets.

@Lessica
Last active March 11, 2024 13:26
Show Gist options
  • Save Lessica/176c2314336fc861398de1e1045aa368 to your computer and use it in GitHub Desktop.
Save Lessica/176c2314336fc861398de1e1045aa368 to your computer and use it in GitHub Desktop.
A Checkbox Cell in NSTableView Column Header
//
// CheckboxHeaderCell.swift
//
// Created by Rachel on 2021/4/26.
// Original: https://stackoverflow.com/questions/11961869/checkbox-in-nstableview-column-header
//
import Cocoa
class CheckboxCell: NSButtonCell {
var alternateState: NSControl.StateValue = .off {
didSet {
super.state = alternateState
}
}
// Ignores the default behavior
override var state: NSControl.StateValue {
get { alternateState }
set { }
}
}
class CheckboxHeaderCell: NSTableHeaderCell {
private lazy var innerCell: CheckboxCell = {
let cell = CheckboxCell()
cell.title = ""
cell.setButtonType(.switch)
cell.type = .nullCellType
cell.isBordered = false
cell.imagePosition = .imageOnly
cell.alignment = .center
cell.objectValue = NSNumber(booleanLiteral: false)
cell.controlSize = .regular
cell.font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .small))
cell.allowsMixedState = true
return cell
}()
// Hide the default "Field" text
override var textColor: NSColor? {
get { .clear }
set { }
}
override var title: String {
get { innerCell.title }
set { innerCell.title = newValue }
}
override var image: NSImage? {
get { innerCell.image }
set { innerCell.image = newValue }
}
// We should not override `state` of this class.
// Instead, a property named `alternateState` is better for accessing its innerCell's state.
var alternateState: NSControl.StateValue {
get { innerCell.alternateState }
set { innerCell.alternateState = newValue }
}
func toggleAlternateState() -> NSControl.StateValue {
innerCell.alternateState = (alternateState != .on ? .on : .off)
return innerCell.alternateState
}
// Override the original -drawWithFrame:inView: will also remove the background or border of the original cell, which is not recommended.
override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) {
super.drawInterior(withFrame: cellFrame, in: controlView)
let centeredRect = CGRect(
x: cellFrame.midX - innerCell.cellSize.width / 2.0,
y: cellFrame.midY - innerCell.cellSize.height / 2.0,
width: innerCell.cellSize.width,
height: innerCell.cellSize.height
)
innerCell.draw(withFrame: centeredRect, in: controlView)
}
}
@Jaycyn
Copy link

Jaycyn commented Mar 2, 2023

This is crashing for me

Copy and pasted the code; have a tableVIew with two columns. XCode 14.2 macOS 12.6.3

Crashes here

override var image: NSImage? {
   get { innerCell.image }   //<- Crash with Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)
   set { innerCell.image = newValue }
}

Any suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment