UIImageView.appearance is overriding UISegmentedControl.appearance - ios

I've been trying to use the appearance proxy API to apply some default colors to some controls, but I've run into a problem.
When I apply a tint color to UISegmentedControl using something like...
UISegmentedControl.appearance().tintColor = UIColor.red
It generates this...
All good, but when I add...
UIImageView.appearance().tintColor = UIColor.green
it changes to...
Just to be clear, I have BOTH this lines in my code
UISegmentedControl.appearance().tintColor = UIColor.red
UIImageView.appearance().tintColor = UIColor.green
It doesn't matter in what order I call them, the result is the same, the UIImageView properties override the UISegmentedControls
I've spent over half a day trying to find a solution to this problem but can't seem to find anything that works.
Running Xcode 8.2, iOS 10, Swift 3
What am I doing wrong and how can I fix?

I am not sure about this, but I guess, UISegmentedControl uses UIImageView to create segments, i.e. the segments we see inside segmented control are UIImageViews and not UIViews. UISegmentedControl even has methods to setImage for a particular segment.
If above is true, we can use appearanceWhenContainedIn API of UIAppearance to set image view tint colour like this:
UIImageView.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).tintColor = UIColor.red
UIImageView.appearance().tintColor = UIColor.green

Related

Swift -How to change the color of the curve on a UISwitch control

How can I change the color of the curve that follows a UISwitch control? I tried changing the borderColor but that's tied to the frame.
This is a sample photo from another question. I want to toggle the color of the silver curve in this photo (not the round button and not the orange background)
I tried these 2 properties but the .layer.borderWidth = 2.0 and .layer.cornerRadius = 15 changes the frame and not the curve
When it's on I want the curve to be .clear and when it's off I want it to be .red
lazy var switchControl: UISwitch = {
let switchControl = UISwitch()
switchControl.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged)
}()
#objc func switchValueDidChange(_ sender: UISwitch) {
if (sender.isOn == true) {
sender.layer.borderColor = UIColor.clear.cgColor // this doesn't work
} else {
sender.layer.borderColor = UIColor.red.cgColor // this doesn't work
}
}
Yes as iChirag said this can not be done as of now using the built-in UISwitch.
As to why it's not possible, it's hard to say with certainty. My guess would be that it's because Apple encourages using a limited color palette in their Human Interface Guidelines. The main goal of using colors in an iOS app is basically to communicate information, help the user differentiate separate parts of the UI at a glance or to call attention to user actions. Maybe they think that setting a custom color for the curve of a UISwitch is outside of these purposes.
With that said, it's fairly easy to build your own custom UISwitch if you need this appearance. Here is a tutorial to get you started, hope this helps:
Make custom UISwitch (Part 1)
Change Tint color of UISwitch
mSwitch.tintColor = offColor// Custom color
I believed this doesn't possible without some hacking tips of SDK. But I am proposing to create your own such control using an image.

Is UISegmentedControl just not usable currently?

Our application has a "dark" palette, with mostly black or charcoal backgrounds. This is creating a major problem in Apple controls that ignore (or don't even offer) control over text and background color.
UISegmentedControl is a particularly good example. It's drawn with often illegible, seemingly arbitrary text/background combinations. These controls are all set up with the exact same properties in IB, and yet you never know if they'll be legible from one view controller to the next.
Most of these are OK in "dark" mode in our app, but "light" mode is shambolic. I've spent a day experimenting with themes, UIAppearance, and setting appearance in IB and programmatically. I'm fed up with it. Does anyone actually know how to guarantee legibility in these things?
Try this,
let seg:UISegmentedControl = {
let seg = UISegmentedControl()
seg.insertSegment(withTitle: "tab 1", at: 0, animated: true)
seg.insertSegment(withTitle: "tab 2", at: 1, animated: true)
seg.selectedSegmentTintColor = .red //you can replace the colours you want
seg.backgroundColor = .lightGray //you can replace the colours you want
return seg
}()
Result
Dark Mode
Light Mode

Change UIAppearance of default UILabels only

I'm building an app with a theme switcher and I'd like to use Appearance to switch the textColor of UILabels that have the default color (black -> white).
I assumed this would be possible and created subclasses for non-default labels, so I simply have to target UILabels with either no subclass or that kept the default color.
When I change the appearance with the following :
UILabel.appearance().textColor = UIColor.white
Every single Label, including subclasses and system labels become white, UIAppearance overriding control-level customization. I feel like it makes sense for UIAppearance to be the default state and for any customization to be applied on top of it.
Would there be any way to use UIAppearance to solve this? Or do I have to manually edit every label I have to add a subclass and a custom property?
Thanks for your help!
The only way I found to overcome this problem is to make the textColor variable read only. But it works:
UILabel.appearance().textColor = UIColor.orange
And the custom class:
class MyLabel: UILabel {
override var textColor: UIColor! {
get { return UIColor.blue }
set {}
}
}

colorWithAlphaComponent example in Swift

What is the correct syntax for this function in Swift?
The following works fine, and colors the background purple:
self.view.backgroundColor = UIColor.purpleColor()
When I chain the colorWithAlphaComponent function, the view shows the correct alpha for a moment, and then changes to an opaque purple that is relatively dark:
self.view.backgroundColor = UIColor.purpleColor().colorWithAlphaComponent(0.5)
Is this the recommended function for adding an alpha value to a UIColor?
Furthermore, why does the intellisense popup say that this function expects a UIColor as a parameter? E.g.,
self.view.backgroundColor = UIColor.colorWithAlphaComponent(<#UIColor#>)
EDIT: The behavior is strange. I am setting the background color on a view controller that is being loaded in a modal. As the modal slides up from the bottom, the alpha is correct. When the modal finishes loading, the background color changes to opaque?!
EDIT 2: The problem was not with the code--both the code above and the suggestion below were properly applying the alpha. The issue is the way that modals are being presented--the underlying view is being removed. See:
Transparent Modal View on Navigation Controller
It's not strange, it's behaving exactly as it should. Although many of UIColor's methods are class methods, there are still a few instance methods, and this is one of them. From the UIColor documentation.
colorWithAlphaComponent:
Creates and returns a color object that has the same color space and component values as the receiver, but has the specified alpha component.
So, colorWithAlphaComponent: just changes the alpha value of its receiver. Example:
let purple = UIColor.purpleColor() // 1.0 alpha
let semi = purple.colorWithAlphaComponent(0.5) // 0.5 alpha
And the reason why you're seeing autocompletion for this instance method on the type, is because Swift allows you to use instance methods as curried type methods. In the example you provided, colorWithAlphaComponent actually returns a function that takes a CGFloat as input and returns a UIColor.
let purple = UIColor.purpleColor()
let purpleFunc: (CGFloat -> UIColor) = UIColor.colorWithAlphaComponent(purple)
So, if you wanted to, you could call the type method passing in the instance you want to modify, and then call the resulting function with the alpha that you want to apply, like so.
let purple = UIColor.purpleColor()
let purpleTrans = UIColor.colorWithAlphaComponent(purple)(0.5)
Then as far as the issues you're having with the modal view controller go, you shouldn't be attempting to change the alpha of the view of a modal view controller. See this for more info. Instead, you should be manually creating a view and adding it to the view hierarchy of your existing view controller (if you absolutely have to alter its alpha)
Swift 5.0
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
in Swift 3.0
This works for me in xcode 8.2.
yourView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
It may helps you.
Since UIColor is part of UIKit, it has been replaced in SwiftUI with Color. The equivalent method is .opacity(_ opacity: Double) for example:
Color.purple.opacity(0.5)
Try smth like this to set color
view.backgroundColor = UIColor(red: (64/255.0), green: (54/255.0), blue: (105/255.0), alpha: 1.0)
UIColor.black.withAlphaComponent(0.5).cgColor

Change colour of dark grey highlight when holding down custom UIButton?

I have a custom UIButton which is a cloud, transparent black and white .png file, with no down state, just one image. When tapping and holding the finger over it, it turns dark grey. I'm trying to change that dark grey to something a little less oppressive. The button is out in the open in a view, not in a tab bar, tool bar, or navigation controller.
I've already tried setting tintColor (which the documentation helpfully informs me is only suitable for 'some' types of buttons, which no indication as to which).
I've also tried changing everything I can find in Interface Builder relating to highlight colours, default states, etc. Nothing has made a difference at all.
I've even tried setting the button's own image for its UIControlStateHighlighted state, but even this causes the dark grey overlay to appear when I hold my finger over it.
How can I change that colour? I've looked at numerous other issues here on SO and have been unable to find a solution that works for me. Any help would be greatly appreciated!
EDIT: I Solved the problem using a category of UIImage which adds a method that uses CoreGraphics to apply a tint to a provided UIImage. I then set THAT image as the highlight, and all is well. Seems a lot of hoop-la to change a colour Apple should've let us change, but c'est la vie.
You said you set a custom image for the UIControlStateHighlighted state. This should disable the default behaviour.
If you still have problems you can disable this effect by setting the adjustsImageWhenHighlighted property to NO and use whatever custom effect you want.
If adjustsImageWhenHighlighted = NO is not working,
set Button-Type to Custom (IB or programmatically).
Default Button-Type: System, changes behavior of highlighted button.
Swift 3:
myButton.adjustsImageWhenHighlighted = false
I was having a similar issue with a custom UIButton when the button was highlighting in grey every time it was pressed. I solved that problem by subclassing UIButton and in the implementation I overrode a single method, (void)setHighlighted: method and kept it empty:
- (void)setHighlighted:(BOOL)highlighted
{
// Leave empty to prevent super from doing whatever
// that it is doing to show the grey highlight.
}
That stopped any type of highlighting as I was not doing anything in the method. It's a better approach if all that you're trying to do is remove any highlighting effect.
So in your code, create a subclass of UIButton, override the setHighlighted method, and then make your custom button a subclass of this custom class.
You can write a custom button that does it
class ActionButton: UIButton {
var originalBackgroundColor: UIColor!
override var backgroundColor: UIColor? {
didSet {
if originalBackgroundColor == nil {
originalBackgroundColor = backgroundColor
}
}
}
override var isHighlighted: Bool {
didSet {
guard let originalBackgroundColor = originalBackgroundColor else {
return
}
backgroundColor = isHighlighted ? originalBackgroundColor.darken() : originalBackgroundColor
}
}

Resources