How can I set title color for a disabled NSButton?(OS X) - swift

I know that we can set a colored title for a button using attributed strings like this:
let style = NSMutableParagraphStyle()
style.alignment = .Center
button.attributedTitle = NSAttributedString(string: "title",
attributes: [ NSForegroundColorAttributeName : NSColor.redColor(),
NSParagraphStyleAttributeName : style])
In fact that's how I'm getting the red title to begin with, but it has no effect on the title color in the disabled mode (it is always gray); I guess in order to set that, I have to create an instance of the NSButton class and override some of its methods related to title color or override some of its properties to disable user interactions(when necessary) instead of disabling the button, but I don't know how.
There are some possible objective-C answers to this question here, here and here.

Create a NSButtonCell Subclass and assign it to the CELL (not the button) in IB.
Then implement
func drawTitle(_ title: NSAttributedString,
withFrame frame: NSRect,
in controlView: NSView) -> NSRect
and return different attributedString for isEnabled or !isEnabled.
You might also set the initial attributedTitle in
init (id)
EDIT: Only works if setWantsLayers is false

Do you need it to be disabled by the definition of it not doing anything when touched?
If so, just have a flag in the button clicked function. So then, the button color could be red (example) when disabled but you can click it but nothing happens. Then if it it's green (example) then it'll allow it to do something.
Your flag could be the color of the button checked through an if statement or set an actual variable.

Related

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

Xcode: Setting UIControl background colour resets on orientation change

In short, I made 'radio button' like buttons. When you press on button, its background gets set to dark green, and the others are set to light green.
However, when I rotate the device (I'm using the emulator) then the colour resets to light green - it's original colour as set up in the .xib file.
Each of the green 'buttons' is a UIControl. I set the colour using
button.backgroundColor = unselectedColour
So, like Alexander suggested, I managed to get this working using a property observer.
Like mentioned above, I have 3 buttons. I also have an enum to help remember which button has been selected (eg. none, button1, button2, button3). I keep a variable to keep track of this enum, with the property observer like so:
/** Remember the last trailer selection made. */
var lastSelection: MyEnum = .none {
willSet {
setButtonColours(selection: newValue)
}
didSet {
}
}
Each button's action sets the lastSelection variable, which then triggers this. Finally, I had to add this little bit of code to make sure that the redraw happens after the orientation change:
override func viewDidLayoutSubviews() {
setButtonColours(selection: lastSelection)
}
I don't know if this is the BEST solution (as I'm a noob), but it works! Thank you again Alexander for the help!

UIAlertController does not turn all elements in the background to B/W

EDIT: Repository with my solution: UIAlertControllerDimmed
After showing UIAlertController, most of the background gets 'dimmed' and turns black and white. Some elements get darker, but don't turn B/W.
These elements are (from top to bottom on the screenshot):
UIImageView inside UINavigationItem
UIButton with red background color and white image
UIImageView inside UITabBarItem
I couldn't find anything related to this topic. What do I have to change to also get these items dimmed?
Here is the without the UIAlertController:
]
I think what's going on here is that you're setting the tintColor of some of the elements and you get different behavior for tintColor than you do for backgroundColor or textColor (or the colors in an image).
When an alert or action sheet appears, iOS 7 automatically dims the
tint color of the views behind it. To respond to this color change, a
custom view subclass that uses tintColor in its rendering should
override tintColorDidChange to refresh the rendering when appropriate.
For example, I created a simple app that displays an alert controller. I set the left button tint color to clear color and the text color to blue:
I set the right button tint color to system green color:
When I run the app and present the alert controller it looks like this
Before:
After:
In order to get the behavior you're looking for, you'll need to follow the advice in #Alexander's answer. You'll need to create grayscale versions of the four images on the screen and animate the transition to them.
You can have a helper function to animate the color change
fileprivate func dimElements(highlight: Bool) {
UIView.animate(withDuration: 0.3) {
self.sendButton.backgroundColor = highlight ? .red : .gray
}
}
and then call it when presenting/dismissing the alert.
let alert = UIAlertController(title: "Error", message: "Oops!", preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .cancel, handler: {_ in self.dimElements(highlight: true) })
alert.addAction(okAction)
self.dimElements(highlight: false)
present(alert, animated: true, completion: nil)
Thank you for your help.
In order to have a more flexible solution I decided to create a subclass of UIAlertController which captures a screenshot, turns it to grayscale colors and inserts it behind the UIAlertController when it gets presented. This way it works without having to do any additional work, and you don't need to implement fade animations for every single element that does not turn to grayscale colors by default.
Github repo: UIAlertControllerDimmed

Why does a NSTextField's field editor inherit the text color of it's target field when it's configured otherwise?

My NSTextField's textColor is white and non-editable. When I click on the label, I set the NSTextField to isEditable = true. Performing a second click on the label places it into edit mode. I get a field editor etc.
Prior or editing it looks like this:
I am subclassing my NSTextField to configure that field editor like so:
override func becomeFirstResponder() -> Bool {
let value = super.becomeFirstResponder()
if value == false {
return value
}
if let fieldEditor = currentEditor() as? NSTextView {
fieldEditor.backgroundColor = NSColor.white
fieldEditor.drawsBackground = true
fieldEditor.textColor = NSColor.black
}
return value
}
You can see from the code above I am setting the textColor of the field editor to black. When I enter editing mode I get the expected behavior:
So far so good. Enter the issue: Any new keystrokes are reflected as white text:
In the sample below I have highlighted the contents of the field to show the white text:
So it would seem that even though I am setting the fieldEditor.textColor = NSColor.black and I see that black color, the field editor is inheriting the NSTextField's text color for new input?
If, in my editor configuration I do this, keep in mind this is in a NSTextField subclass:
fieldEditor.backgroundColor = NSColor.white
fieldEditor.drawsBackground = true
fieldEditor.textColor = NSColor.black
textColor = NSColor.black // <-------
Then text input while in editing mode does come though as black. I understand the field editor is a proxy to the NSTextField:
https://developer.apple.com/library/content/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html#//apple_ref/doc/uid/TP40009459-CH3-SW29
So is the flow:
field editor shows
becomes first responder
has some kind of view for the first render
sends keystrokes to the underlying NSTextField
updates itself using the NSTextField settings?