自定义NSTableCellView文字被动态条形遮挡,如何用CGBlendMode.exclusion处理?
解决NSTableCellView动态条形遮挡文本的问题
问题背景
我自定义了NSTableCellView,单元格内有随数值动态变化长度的彩色条形(范围0-100%),但黑色文本落在黑色条形上时会被遮挡。已知用.exclusion混合模式可以解决这个问题,但之前从未实现过该功能,代码库也没有同类实现。参考PDF页面渲染的自定义高亮代码写了部分代码,却不知道如何正确设置上下文及完成实际绘制(当时上下文由PDFKit提供)。
当前代码:
class BarTableCellView: NSTableCellView { @IBOutlet var bar1: NSBox! @IBOutlet var bar1Width: NSLayoutConstraint! @IBOutlet var bar2: NSBox! @IBOutlet var bar2Width: NSLayoutConstraint! override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // Drawing code here. let context = NSGraphicsContext.current context?.saveGraphicsState() context.setBlendMode(CGBlendMode.exclusion) } }
解决方案
核心是调整绘制顺序,先绘制条形,再用exclusion混合模式绘制文本,同时严格管理图形状态,避免影响其他视图绘制。
修改后的完整代码:
class BarTableCellView: NSTableCellView { @IBOutlet var bar1: NSBox! @IBOutlet var bar1Width: NSLayoutConstraint! @IBOutlet var bar2: NSBox! @IBOutlet var bar2Width: NSLayoutConstraint! override func draw(_ dirtyRect: NSRect) { guard let context = NSGraphicsContext.current?.cgContext else { super.draw(dirtyRect) return } // 保存初始图形状态 context.saveGState() // 先绘制基础元素(包括条形和默认文本) super.draw(dirtyRect) // 恢复初始状态后,设置exclusion混合模式 context.restoreGState() context.saveGState() context.setBlendMode(.exclusion) // 重新绘制文本,应用混合模式 if let textField = textField, let text = textField.stringValue as NSString? { let textAttrs = textField.attributedStringValue.attributes(at: 0, effectiveRange: nil) text.draw(in: textField.frame, withAttributes: textAttrs) } // 恢复图形状态,避免污染其他绘制操作 context.restoreGState() } }
关键细节
- 绘制顺序:必须让条形先完成绘制,再在混合模式下绘制文本,这样重叠部分会触发
exclusion效果——黑色文本在黑色条形上会显示为白色,保证文本可读性。 - 图形状态管理:每次修改上下文属性前调用
saveGState(),绘制完成后调用restoreGState(),防止上下文设置影响其他视图的正常绘制。 - 手动重绘文本:由于
super.draw()已经默认绘制了一次文本,需要在混合模式下重新绘制一次文本,覆盖原有效果,实现混合模式的应用。
如果NSBox条形的层级在文本上方,也可以直接调整子视图顺序(将文本字段移到条形视图上层),再结合上述混合模式设置,能达到同样效果。
内容的提问来源于stack exchange,提问作者Jc Nolan
相关产品推荐
相关产品推荐

