如果您绝对需要更改样式,我建议您使用第 3 方自定义控件,让您可以根据需要自定义外观;一些可行的建议可以找到here。从 10.15 开始,NSSwitch 根本没有您想要的自定义。如果您不在乎细节,请立即停止阅读?
也就是说,剖析NSSwitch 的组件很有趣。与许多 Cocoa 控件不同,此控件没有支持 NSCell。文档明确说明了这一点:
NSSwitch 不使用 NSCell 的实例来提供其功能。 cellClass 类属性和单元格实例属性都返回 nil,它们会忽略设置非 nil 值的尝试。
因此,它是一个相当现代的控件,由绘制内容的层组成。有 3 个 NSWidgetViews 是私有的 NSView 子类。
这些视图主要利用-[NSView updateLayer] 来根据通过-[NSWidgetView setWidgetDefinition:] 提供的内容提取其支持的CALayer 所需值。此方法传入一个值字典,该字典定义了NSWidgetView 应如何绘制到图层中。最后视图的示例字典:
{
kCUIPresentationStateKey = kCUIPresentationStateInactive;
kCUIScaleKey = 2;
kCUIUserInterfaceLayoutDirectionKey = kCUIUserInterfaceLayoutDirectionLeftToRight;
size = regular;
state = normal;
value = 0;
widget = kCUIWidgetSwitchFill;
}
不幸的是,这意味着样式主要由widget = kCUIWidgetSwitchFill; 所见的预定义字符串确定,它完全涉及如何根据系统颜色(暗/亮)或突出显示颜色绘制填充颜色或禁用颜色。颜色与 NSAppearance 相关联,并且没有明确的方法可以覆盖其中任何一个。
一个(不推荐,真的不要这样做)解决方案是调出updateLayer 对 NSWidgetView 的调用,并在您需要的情况下进行自己的附加层自定义。用 Swift 编写的示例:
/// Swizzle out NSWidgetView's updateLayer for our own implementation
class AppDelegate: NSObject, NSApplicationDelegate {
/// Swizzle out NSWidgetView's updateLayer for our own implementation
func applicationWillFinishLaunching(_ notification: Notification) {
let original = Selector("updateLayer")
let swizzle = Selector("xxx_updateLayer")
if let widgetClass = NSClassFromString("NSWidgetView"),
let originalMethod = class_getInstanceMethod(widgetClass, original),
let swizzleMethod = class_getInstanceMethod(NSView.self, swizzle) {
method_exchangeImplementations(originalMethod, swizzleMethod)
}
}
}
extension NSView {
@objc func xxx_updateLayer() {
// This calls the original implementation so all other NSWidgetViews will have the right look
self.xxx_updateLayer()
guard let dictionary = self.value(forKey: "widgetDefinition") as? [String: Any],
let widget = dictionary["widget"] as? String,
let value = (dictionary["value"] as? NSNumber)?.intValue else {
return
}
// If we're specifically dealing with this case, change the colors and remove the contents which are set in the enabled switch case
if widget == "kCUIWidgetSwitchFill" {
layer?.contents = nil;
if value == 0 {
layer?.backgroundColor = NSColor.red.cgColor;
} else {
layer?.backgroundColor = NSColor.yellow.cgColor;
}
}
}
}
再一次,不要这样做!您将来肯定会崩溃,Apple 将在 App Store 提交中拒绝这一点,并且您可以使用自定义控件更加安全,该控件完全符合您的要求,而无需使用私有 API。