ThemeKit
macOS theming library
Install / Use
/learn @luckymarmot/ThemeKitREADME
Summary
ThemeKit is a lightweight theming library completely written in Swift that provides theming capabilities to both Swift and Objective-C macOS applications.
ThemeKit is brought to you with ❤️ by Nuno Grilo and the Paw team.
<p align="left"> <img src="https://github.com/luckymarmot/ThemeKit/raw/master/Imgs/ThemeKit.gif" width="675" height="378" alt="ThemeKit Animated Demo" /> </p>QuickStart
- Download the ThemeKit Demo binary and give it a try!
- Read the Make your macOS app themable article (simple tutorial).
- Check the ThemeKit Docs.
Table of Contents
Features
- Written in Swift 4.2
- Optional configuration, none required
- Neglected performance impact
- Automatically theme windows (configurable)
- Themes:
LightTheme(default macOS appearance)DarkThemeSystemTheme(default theme). Dynamically resolves toThemeManager.lightThemeorThemeManager.darkTheme, depending on the "System Preferences > General > Appearance".- Support for custom themes (
Theme) - Support for user-defined themes (
UserTheme)
- Theme-aware assets:
ThemeColor: colors that dynamically change with the themeThemeGradient: gradients that dynamically change with the themeThemeImage: images that dynamically change with the theme- Optional override of
NSColornamed colors (e.g.,labelColor) to dynamically change with the theme
Installation
|ThemeKit Version|Swift Version| |----------------|-------------| |1.0.0 | 3.0 | |1.1.0 | 4.0 | |1.2.0 | 4.1 | |1.2.3 | 4.2 |
There are multiple options to include ThemeKit on your project:
-
Add to your
Podfile:use_frameworks! target '[YOUR APP TARGET]' do pod 'macOSThemeKit', '~> 1.2.0' endWhen using CocoaPods, the ThemeKit module is named
macOSThemeKit:import macOSThemeKit -
github "luckymarmot/ThemeKit"Then import ThemeKit module with:
import ThemeKit -
Manually
- Either add
ThemeKit.frameworkon your project, or, manually add source files from theThemeKit\folder to your project - If importing into a Objective-C project, you will need to include all the Swift related frameworks as well (as reported here)
Then import ThemeKit module with:
import ThemeKit - Either add
Usage
Simple Usage
At its simpler usage, applications can be themed with a single line command:
In Swift:
func applicationWillFinishLaunching(_ notification: Notification) {
/// Apply the dark theme
ThemeManager.darkTheme.apply()
/// or, the light theme
//ThemeManager.lightTheme.apply()
/// or, the 'system' theme, which dynamically changes to light or dark,
/// respecting *System Preferences > General > Appearance* setting.
//ThemeManager.systemTheme.apply()
}
In Objective-C:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
// Apply the dark theme
TKDarkTheme *darkTheme = TKThemeManager.darkTheme;
[[TKThemeManager sharedManager] setTheme:darkTheme];
}
Advanced Usage
The following code will define which windows should be automatically themed (WindowThemePolicy) and add support for user themes (UserTheme):
In Swift:
func applicationWillFinishLaunching(_ notification: Notification) {
/// Define default theme.
/// Used on first run. Default: `SystemTheme`.
/// Note: `SystemTheme` is a special theme that resolves to `ThemeManager.lightTheme` or `ThemeManager.darkTheme`,
/// depending on the macOS preference at 'System Preferences > General > Appearance'.
ThemeManager.defaultTheme = ThemeManager.lightTheme
/// Define window theme policy.
ThemeManager.shared.windowThemePolicy = .themeAllWindows
//ThemeManager.shared.windowThemePolicy = .themeSomeWindows(windowClasses: [MyWindow.self])
//ThemeManager.shared.windowThemePolicy = .doNotThemeSomeWindows(windowClasses: [NSPanel.self])
//ThemeManager.shared.windowThemePolicy = .doNotThemeWindows
/// Enable & configure user themes.
/// Will use folder `(...)/Application Support/{your_app_bundle_id}/Themes`.
let applicationSupportURLs = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true)
let thisAppSupportURL = URL.init(fileURLWithPath: applicationSupportURLs.first!).appendingPathComponent(Bundle.main.bundleIdentifier!)
let userThemesFolderURL = thisAppSupportURL.appendingPathComponent("Themes")
ThemeManager.shared.userThemesFolderURL = userThemesFolderURL
/// Change the default light and dark theme, used when `SystemTheme` is selected.
//ThemeManager.lightTheme = ThemeManager.shared.theme(withIdentifier: PaperTheme.identifier)!
//ThemeManager.darkTheme = ThemeManager.shared.theme(withIdentifier: "com.luckymarmot.ThemeKit.PurpleGreen")!
/// Apply last applied theme (or the default theme, if no previous one)
ThemeManager.shared.applyLastOrDefaultTheme()
}
In Objective-C:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
/// Define default theme.
/// Used on first run. Default: `SystemTheme`.
/// Note: `SystemTheme` is a special theme that resolves to `ThemeManager.lightTheme` or `ThemeManager.darkTheme`,
/// depending on the macOS preference at 'System Preferences > General > Appearance'.
[TKThemeManager setDefaultTheme:TKThemeManager.lightTheme];
/// Define window theme policy.
[TKThemeManager sharedManager].windowThemePolicy = TKThemeManagerWindowThemePolicyThemeAllWindows;
//[TKThemeManager sharedManager].windowThemePolicy = TKThemeManagerWindowThemePolicyThemeSomeWindows;
//[TKThemeManager sharedManager].themableWindowClasses = @[[MyWindow class]];
//[TKThemeManager sharedManager].windowThemePolicy = TKThemeManagerWindowThemePolicyDoNotThemeSomeWindows;
//[TKThemeManager sharedManager].notThemableWindowClasses = @[[NSPanel class]];
//[TKThemeManager sharedManager].windowThemePolicy = TKThemeManagerWindowThemePolicyDoNotThemeWindows;
/// Enable & configure user themes.
/// Will use folder `(...)/Application Support/{your_app_bundle_id}/Themes`.
NSArray<NSString*>* applicationSupportURLs = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSURL* thisAppSupportURL = [[NSURL fileURLWithPath:applicationSupportURLs.firstObject] URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier];
NSURL* userThemesFolderURL = [thisAppSupportURL URLByAppendingPathComponent:@"Themes"];
[TKThemeManager sharedManager].userThemesFolderURL = userThemesFolderURL;
/// Change the default light and dark theme, used when `SystemTheme` is selected.
//TKThemeManager.lightTheme = [[TKThemeManager sharedManager] themeWithIdentifier:PaperTheme.identifier];
//TKThemeManager.darkTheme = [[TKThemeManager sharedManager] themeWithIdentifier:@"com.luckymarmot.ThemeKit.PurpleGreen"];
/// Apply last applied theme (or the default theme, if no previous one)
[[TKThemeManager sharedManager] applyLastOrDefaultTheme];
}
Please check the Demo application source code for a more complete usage example of ThemeKit.
Observing theme changes
ThemeKit provides the following notifications:
Notification.Name.willChangeThemeis sent when current theme is about to changeNotification.Name.didChangeThemeis sent when current theme did changeNotification.Name.didChangeSystemThemeis sent when system theme did change (System Preference > General)
Example:
// Register to be notified of theme changes
NotificationCenter.default.addObserver(self, selector: #selector(changedTheme(_:)), name: .didChangeTheme, object: nil)
@objc
