ThemeColor

open class ThemeColor: NSColor

ThemeColor is a NSColor subclass that dynamically changes its colors whenever a new theme is make current.

Theme-aware means you don’t need to check any conditions when choosing which color to draw or set on a control. E.g.:

myTextField.textColor = ThemeColor.myContentTextColor

ThemeColor.myCircleFillColor.setFill()
NSBezierPath(rect: bounds).fill()

The text color of myTextField will automatically change when the user switches a theme. Similarly, the drawing code will draw with different color depending on the selected theme. Unless some drawing cache is being done, there’s no need to refresh the UI after changing the current theme.

You can also define a color to be a pattern image using NSColor(patternImage:).

Defining theme-aware colors

The recommended way of adding your own dynamic colors is as follows:

  1. Add a ThemeColor class extension (or TKThemeColor category on Objective-C) to add class methods for your colors. E.g.:

    In Swift:

    extension ThemeColor {
    
      static var brandColor: ThemeColor { 
        return ThemeColor.color(with: #function)
      }
    
    }
    

    In Objective-C:

    @interface TKThemeColor (Demo)
    
    + (TKThemeColor*)brandColor;
    
    @end
    
    @implementation TKThemeColor (Demo)
    
    + (TKThemeColor*)brandColor {
      return [TKThemeColor colorWithSelector:_cmd];
    }
    
    @end
    
  2. Add Class Extensions on any Theme you want to support (e.g., LightTheme and DarkTheme - TKLightTheme and TKDarkTheme on Objective-C) to provide instance methods for each theme color class method defined on (1). E.g.:

    In Swift:

    extension LightTheme {
    
      var brandColor: NSColor {
        return NSColor.orange
      }
    
    }
    
    extension DarkTheme {
    
      var brandColor: NSColor {
        return NSColor.white
      }
    
    }
    

    In Objective-C:

    @interface TKLightTheme (Demo) @end
    
    @implementation TKLightTheme (Demo)
    
       - (NSColor*)brandColor
       {
           return [NSColor orangeColor];
       }
    
    @end
    
    @interface TKDarkTheme (Demo) @end
    
    @implementation TKDarkTheme (Demo)
    
       - (NSColor*)brandColor
       {
           return [NSColor whiteColor];
       }
    
    @end
    
  3. If supporting UserTheme‘s, define properties on user theme files (.theme) for each theme color class method defined on (1). E.g.:

    displayName = Sample User Theme
    identifier = com.luckymarmot.ThemeKit.SampleUserTheme
    darkTheme = false
    
    brandColor = rgba(96, 240, 12, 0.5)
    

Overriding system colors

Besides your own colors added as ThemeColor class methods, you can also override NSColor class methods so that they return theme-aware colors. The procedure is exactly the same, so, for example, if adding a method named labelColor to a ThemeColor extension, that method will be overriden in NSColor and the colors from Theme subclasses will be used instead. In sum, calling NSColor.labelColor will return theme-aware colors.

You can get the full list of available/overridable color methods (class methods) calling NSColor.colorMethodNames().

At any time, you can check if a system color is being overriden by current theme by checking the NSColor.isThemeOverriden property (e.g., NSColor.labelColor.isThemeOverriden).

When a theme does not override a system color, the original system color will be used instead. E.g., you have overrided ThemeColor.labelColor, but currently applied theme does not implement labelColor -> original labelColor will be used.

Fallback colors

With the exception of system overrided named colors, which defaults to the original system provided named color when theme does not specifies it, unimplemented properties/methods on target theme class will default to fallbackForegroundColor and fallbackBackgroundColor, for foreground and background colors respectively. These too, can be customized per theme.

Please check ThemeGradient for theme-aware gradients and ThemeImage for theme-aware images.

  • ThemeColor color selector used as theme instance method for same selector or, if inexistent, as argument in the theme instance method themeAsset(_:).

    Declaration

    Swift

    @objc public var themeColorSelector: Selector = #selector(getter: NSColor.clear)
  • Resolved color from current theme (dynamically changes with the current theme).

    Declaration

    Swift

    @objc public lazy var resolvedThemeColor: NSColor = NSColor.clear
  • Create a new ThemeColor instance for the specified selector.

    Returns a color returned by calling selector on current theme as an instance method or, if unavailable, the result of calling themeAsset(_:) on the current theme.

    Declaration

    Swift

    public class func color(with selector: Selector) -> ThemeColor

    Parameters

    selector

    Selector for color method.

    Return Value

    A ThemeColor instance for the specified selector.

  • Color for a specific theme.

    Declaration

    Swift

    public class func color(for theme: Theme, selector: Selector) -> NSColor

    Parameters

    theme

    A Theme instance.

    selector

    A color selector.

    Return Value

    Resolved color for specified selector on given theme.

  • Current theme color, but respecting view appearance and any window specific theme (if set).

    If a NSWindow.windowTheme was set, it will be used instead. Some views may be using a different appearance than the theme appearance. In thoses cases, color won’t be resolved using current theme, but from either lightTheme or darkTheme, depending of whether view appearance is light or dark, respectively.

    Declaration

    Swift

    public class func color(for view: NSView, selector: Selector) -> NSColor

    Parameters

    view

    A NSView instance.

    selector

    A color selector.

    Return Value

    Resolved color for specified selector on given view.

  • Forces dynamic color resolution into resolvedThemeColor and cache it. You should not need to manually call this function.

    Declaration

    Swift

    @objc open func recacheColor()
  • Clear all caches. You should not need to manually call this function.

    Declaration

    Swift

    @objc class open func emptyCache()