Xcode: Setting UIControl background colour resets on orientation change - swift

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!

Related

Customising the sidebar with NSTintConfiguration

In the Adopt the new look of macOS video, you can use NSTintConfiguration to customise the sidebar icon colour using this code.
func outlineView(_ outlineView: NSOutlineView, tintConfigurationForItem item: Any) -> NSTintConfiguration? {
if case let sectionItem as SectionItem = item {
/*
This outline view uses a type called "SectionItem" to populate its top-level sections.
Here we choose a tint configuration based on a hypothetical `isSecondarySection` property on that type.
*/
return sectionItem.isSecondarySection ? .monochrome : .default
}
// For all other cases, a return value of `nil` indicates that the item should inherit a tint from its parent.
return nil
}
When I tried to force a colour on my app sidebar, let's say yellow for an example like this.
func outlineView(_ outlineView: NSOutlineView, tintConfigurationForItem item: Any) -> NSTintConfiguration? {
return .init(fixedColor: .systemYellow)
}
It (sort of) worked as intended, meaning that any rows that are not selected are customised as intended but when you select a row, the SF Symbols changes colour. I expected it to be yellow.
This is how it looks. You can see the icon tint colours are indeed yellow but only if you do not select them.
What I want is the highlighted row tint colour to be the same as the unselected rows. Like how Apple does it in their apps.
You’re seeing the green accent color row selection highlight (NSTableRowView.emphasized = true) and white icon because your outline view is currently the window’s first responder. If you select a different control outside of the outline view, like an NSTextField in the window’s main content area for example, the row selection will change to the inactive appearance (emphasized = false). This is independent of the new tint configuration API.
Mail and Finder go to some effort to prevent their sidebar outline views from becoming first responder in most situations, so they usually has the inactive appearance like in your second screenshot. You can approximate this behaviour by setting refusesFirstResponder = true on your outline view or by subclassing NSOutlineView and overriding acceptsFirstResponder if you need more fine-grained control.

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
}
}
}

Making the title bar of a window completely transparent

i'm working on an macos app in Swift 3, where I'd like to make the title bar transparent, and not show the title of my app, so basically, just the 3 buttons (close, minify, fullscreen) on my background.
What I tried is to put the following in the viewDidLoad method of the first view controller that is being used:
self.view.window?.styleMask.insert(NSWindowStyleMask.unifiedTitleAndToolbar)
self.view.window?.styleMask.insert(NSWindowStyleMask.fullSizeContentView)
self.view.window?.styleMask.insert(NSWindowStyleMask.titled)
self.view.window?.toolbar?.isVisible = false
self.view.window?.titleVisibility = .hidden
self.view.window?.titlebarAppearsTransparent = true
but what I end up with is this:
it seems like the title bar has got a lower opacity then normal, but I'd like it (and the app title) gone completely.
Am I missing something or is this impossible?
Thanks.
The problem is that inside viewDidLoad method the window property is always nil. All the optional chaining in your code just fails silently. You just need to move your code to viewWillAppear method.
override func viewWillAppear() {
super.viewWillAppear()
// configure your window properties here
}

How to Disable Multi Touch on UIBarButtonItems & UI Buttons?

My app has quite a few buttons on each screen as well as a UIBarButtonItem back button and I have problems with people being able to multi click buttons. I need only 1 button to be clickable at a time.
Does anyone know how to make a UIBarButtonItem back button exclusive to touch?
I've managed to disable multi clicking the UIButtons by setting each one's view to isExclusiveTouch = true but this doesn't seem to count for the back button in the navigation bar.
The back button doesn't seem to adhere to isExclusiveTouch.
Does anyone have a simple work around that doesn't involve coding each and every buttons send events?
Many Thanks,
Krivvenz.
you can enable exclusive touch simply this will stop multiple touch until first touch is not done
buttton.exclusiveTouch = true
You could write an extension for UIBarButtonItem to add isExclusiveTouch?
you can simply disable the multi-touch property of the super view. You can also find this property in the storyboard.
you could try this for the scene where you want to disable multiple touch.
let skView = self.view as! SKView
skView.isMultipleTouchEnabled = false
I have found a solution to this. isExclusiveTouch is the solution in the end, but the reason why this property didn't do anything is because you need to set it also on all of the subviews of each button that you want to set as isExclusiveTouch = true. Then it works as expected :)
It's working fine in Swift
self.view.isMultipleTouchEnabled = false
buttonHistory.isExclusiveTouch = true
In addition of #Luky Lízal answer, you need pay attention that all subviews of a view you want disable multi-touching (exactly all in hierarchy, actually subviews of subviews) must be set as isExclusiveTouch = true.
You can run through all of them recursively like that:
extension UIView
{
func allSubViews() -> [UIView] {
var all: [UIView] = []
func getSubview(view: UIView) {
all.append(view)
guard view.subviews.count > 0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
}
// Call this method when all views in your parent view were set
func disableMultipleTouching() {
self.isMultipleTouchEnabled = false
self.allSubViews().forEach { $0.isExclusiveTouch = true }
}

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

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.