How can I read the user selection for OS X menu bar in Xcode (Swift) - swift

I'm creating an agent app for OS X in swift (only showing the app icon in the menu bar). I'm loading the icon for the app from the AppDelegate using:
statusItem.image = NSImage(named: "BlackIcon")
and it works fine.
However, if the user has chosen to use the dark menu bar from the System Preferences -> General, the user won't see the icon as it's black.
So I need to display a different 'WhiteIcon' to the user if they have the option selected.
How can I check whether the user has this option active from my app?

With Swift 3.0, you can use the UserDefaults to access the macOS appearance, or "AppleInterfaceStyle", using the following code:
let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
If the user has enabled dark mode, the defaults will return a string "Dark". If they have the "light mode" enabled it will return nil. So you will need to wrap that in the following code:
if UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" {
statusItem.image = NSImage(named: "WhiteIcon")
} else {
statusItem.image = NSImage(named: "BlackIcon")
}
I'm sure there might be a cleaner way, maybe with a guard but that will get you where you need to be in Swift 3.0
Edit:
The above code will determine the users current "mode". However, using a simple check for the users preference will not result in the correct behavior (e.g. it will only fire when the application starts).
The correct method of performing this is to set the menu icon as a black icon. Then, browse to the asset in the Asset Catalog, and select the menu icon. With the menu selected, browse to the Attributes Inspector and make sure the image is checked with a “Mac” device. Then choose “Render As” set to “Template Image”.
This only requires you to have one icon, in black, and macOS will handle the conversion of the images from dark to light modes.

It appears that you are trying to invert menulet icon color for dark mode. By default OSX handles darkmode and inverts the image color, however you need to specifically add [image setTemplate:YES] to have this work for you if it already doesnt.
Objective-c:
self.statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSSquareStatusItemLength];
NSImage *image = [NSImage imageNamed:#"statusItemIcon"];
[image setTemplate:YES];
[self.statusItem setImage:image];
swift: (Originally answered by Zhi-Wei Cai at link below)
var isDark = false
func isDarkMode() {
// Swift2
// isDark = NSAppearance.currentAppearance().name.hasPrefix("NSAppearanceNameVibrantDark")
// Swift3+
isDark = NSAppearance.current.name.rawValue.hasPrefix("NSAppearanceNameVibrantDark")
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
isDarkMode()
// Now use "isDark" to determine the drawing colour.
if isDark {
// ...
} else {
// ...
}
}
This answer explains it in the detail: NSStatusItem change image for dark tint

Related

Background color of NSButton doesn't changes & option not present in XCode

Unlike my colleague, who has Bezel color option , I don't have it in my xcode
& when even when I try to use this SO approach by changing colors from creating a "Key Path", it doesn't change nor does it change with swift code in VC:
let button = NSButton()
button.layer?.backgroundColor = NSColor.red.cgColor

How to remove displayImage tint in Edit Widget view?

I have an IntentHandler where I am setting the displayImage value for the configuration options being provided to my app widget.
On the 'Edit Widget' screen (accessed by long-pressing the Widget), a tint is being applied that is rendering the image entirely blue:
If I tap the value to see all available options, the images are rendered normally.
I suspect the tint is due to the image being part of a button, but as far as I'm aware I don't have direct access to the button to change its options.
Here is a simplified version of the IntentHandler class:
class IntentHandler: INExtension, ConfigurationIntentHandling {
func provideMyDataOptionsCollection(for intent: ConfigurationIntent, with completion: #escaping (INObjectCollection<MyData>?, Error?) -> Void) {
var dataForWidget = [MyData]()
// Retrieve dynamic data here...
let myData = MyData(identifier: String(id), display: name)
// Retrieve corresponding image here...
myData.displayImage = INImage(imageData: (retrievedImage.pngData())!)
dataForWidget.append(myData)
let collection = INObjectCollection(items: dataForWidget)
completion(collection, nil)
}
What is the best way around this?
I was having the exact same issue and came across this post - How do I turn off the blue overlay on the image I'm using with my UIButton?
I ended up following the 2nd approach and going to my Asset Catalog and changing the image's default rendering type to "Original" for the affected images.
This removed the blue tint on the widget customization screen when a selection is made.

How to get rid of the white shadow in dark mode in macOS?

I am making a macOS app in Swift and am seeing a weird issue when I switch b/w light and dark modes. The shadow that appears around the image on the light mode is great but the one I am getting by default on the dark mode doesn't look nice. Does anyone know how to fix this? I am getting the same for all other UI elements like Checkboxes, Radio Buttons, etc.
Not nice
Nice
Note:
This image is added via the Interface Builder in Xcode.
I have the shadow checkbox unchecked as well. See below:
Make sure that the superviews of your view don't define a shadow either or that it has an opaque background.
If your superview doesn't have an opaque background, child views will also have a shadow.
You can either try implementing viewDidChangeEffectiveAppearance() on your NSView or do key-value observations on NSApp.effectiveAppearance. This way you'll be notified about appearance changes between light/dark mode so you can react accordingly.
You can detect whether a view is Dark Mode or not. And you hide shadow when in dark mode.
Add an extension to NSView:
extension NSView {
func isDarkMode() -> Bool {
if #available(OSX 10.14, *) {
return effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
}
return false
}
}
Create a custom view (view, button etc). Override viewDidChangeEffectiveAppearance method in your custom view and update layer.
class CustomView: NSView {
override func viewDidChangeEffectiveAppearance() {
// edit shadow's properties. ex: shadowOpacity.
if isDarkMode() {
// dark mode
} else {
// light mode
}
}
}

How to implement DarkMode Into app in Swift

sorry if that question was asked but couldn't find the right answer across stackOverFlow so I'm asking ..
I'm trying to implement dark mode into my app, but unfortunately it doesn't work well for me while using tableviews, it does changes my background and stuff, but I can't change the color of my groups in my tableview.
Here's an image to illustrate the problem:
https://imgur.com/a/h4A3zOZ (can't upload it here cause its too big).
Also Here is my Code:
// MARK: - Premium Section - DarkMode + Graph:
#IBAction func darkModeSwitch(_ sender: UISwitch) {
let current = sender.isOn ? Theme.dark : Theme.light
if #available(iOS 13.0, *) {
// overrideUserInterfaceStyle = UIUserInterfaceStyle(rawValue: current.stateMode)!
//STEP1: Saving User Defaults Switcher:
saveSwitchToggleDarkMode(switcherState: sender.isOn)
//STEP2: Setting UI Colors Of Settings View:
self.tableView.backgroundColor = current.backgroundColor
///Setting up the barTint Color:
self.navigationController?.navigationBar.barTintColor = current.barTintColor
///Setting up the title text color:
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor:current.textColor]
///Changing back color in navigation controller:
self.navigationController?.navigationBar.backItem?.backBarButtonItem?.tintColor = current.backItemColor
}
}
You should change the mode on the window's level to apply changes to all your controls e.g:
if #available(iOS 13, *) {
UIApplication.shared.delegate?.window??.overrideUserInterfaceStyle = .dark
}
An alternative (and perhaps easier) method to implement dark mode is to use the iOS dark mode feature that you can trigger in settings.If you want to implement this you can create a custom color set by going to your Assets.xcassets and pressing the plus mark on the bottom -> new color set. On the attributes inspector, name your color under name, and under Appearances, select 'Any, Light, Dark' now you will have a place for 3 different colors. Under Light, put the light mode color, on the dark, the dark mode color.
Then on the place where you wish to implement this color,you can change the color to your custom color in the storyboard like so :-
or you can change it in code with something like
myButton.backgroundColor = UIColor(named: "TestColor")
When the user triggers the Dark mode through their control center or settings, the app will also automatically change accordingly. You can test this by going to settings -> Developer -> Dark appearance or by going to Features -> Toggle Appearance or simply press Shift + Command + A
However this method means that you will not have an independent dark mode because it will only be triggered if the device itself is dark-mode enabled.

Default tableview cells don't respond to dark mode

Using a custom cell I'm able to get dark mode/normal mode to work properly. But when using the default framework cell Apple has provided it remains white regardless of what mode I enable. I read here
ios13 Dark Mode change not recognized by tableview Cell?
about the same problem. The answer tells me to use this:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
removeAndReaddGradientIfNeeded()
}
}
But I'm unsure how exactly I'm supposed to use this and how it relates to my cells. My code right now for my cells is this:
if #available(iOS 13, *) {
cell.backgroundColor = UIColor.systemBackground
cell.textLabel?.textColor = UIColor(named: "MainLabelColor")
cell.detailTextLabel?.textColor = UIColor(named: "SubLabelColor")
}
I use system color and custom colors in assets with two modes, one for light and one for dark. Now, this works fine in custom cell, but not in default.
Could anyone show me how to use the delegate function with cells?
Did you try to change the contentView background color? because the content view sits on top of the cell.
if #available(iOS 13, *) {
cell.contentView.backgroundColor = UIColor.systemBackground
//For named color you have to resolve it.
cell.textLabel?.textColor = UIColor(named: "MainLabelColor")?.resolvedColor(with: self.traitCollection)
cell.detailTextLabel?.textColor = UIColor(named: "SubLabelColor")?.resolvedColor(with: self.traitCollection)
//MARK:- Even If your Viewcontroller disabled dark mode, tableView cell will be enabled.
self.overrideUserInterfaceStyle = .unspecified
}
To Support Dark Mode make sure you removed following overrides:-
UserInterfaceStyle default value is unspecified . So, You might have enabled userInterfaceStyle to light in somewhere in your code or list file.
In Plist file check for following key-value and remove them:-
<key>UIUserInterfaceStyle</key>
<string>light</string>
In Code check for the following the line and remove them.
i) If the key window is overridden to light mode, your entire app will be forced to light mode.
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .light
ii) If View Controller is overridden to light mode, your entire ViewController will be forced to light mode.
self.overrideUserInterfaceStyle = .light