🐰
AppKitやCore Animationのカラーに現在のアピアランス(ダークモード等)を反映する
extension
macOS 11 BigSur以降のシステムではNSAppearanceのメソッドperformAsCurrentDrawingAppearance(_ block: () -> Void)
を使って色を更新するコードを括ることが推奨されていますが、それより前のシステムでは、以下のようにeffectiveAppearance
を手動で差し替える等のテクニックにより対処します。
なお、NSAppearance.current
はmacOS 13以降では非推奨となっています。
extension NSAppearance {
/// アピアランスを適用
static func perform(_ closure: () -> (Void)) {
if #available(macOS 11.0, *) {
NSApp.effectiveAppearance.performAsCurrentDrawingAppearance(closure)
}
else {
let prevAppearance = NSAppearance.current
NSAppearance.current = NSApp.effectiveAppearance
defer {
NSAppearance.current = prevAppearance
}
closure()
}
}
/// ダークモード判定
static func isDarkMode() -> Bool {
if #available(macOS 10.14, *) {
return NSApp.effectiveAppearance.bestMatch(from: [.aqua, .darkAqua]) == .darkAqua
}
return false
}
}
使用
以下のコードを適切な場所で実行すると、現在のアピアランスに基づくカラーを反映することができます。基本的には色を反映する箇所ではこのクロージャで該当コードを括っておけば良いと思われます。
※再描画が実行される代表的なメソッド等。NSViewならdrawRect(_:)
、CALayerならdisplay()
など。いずれもsetNeedsDisplay()
のような更新フラグを立てるメソッドを使って、システムが適切な時期に実行する。
// CALayerの背景に現在のアピアランスに基づくシステムカラーを適用
NSAppearance.perform {
layer.backgroundColor = NSColor.systemRed.cgColor
}
// NSTextViewのテキストに現在のアピアランスに基づくシステムカラーを適用
NSAppearance.perform {
textView.textColor = NSColor.secondaryLabelColor
}
参考
Discussion