Runtime attributes border color - ios

I use defined runtime attributes in button.
layer.cornerRadius
layer.masksToBounds
layer.borderWidth
And I want to paint my border in green colour. But my code doesn’t work:
layer.borderUIColor
Borders have black colour. How to paint border in colour with runtime attributes?

Actually, you are using wrong attribute.The, correct attribute is layer.borderColor.
But again it will not work because it is type of CGColor and from IB we can only assign UIColor, we can't assign CGColor.
Eihter you can simply do it programaticlly.
Or
You can create extension with type CGColor.

Order also matters for runtime attributes.
I use following and it worked for me:

Xcode 13.4, MacOS 12.4, 07082022
This is the answer that creatively solved the problem without requiring a computed property. It works with layer.borderColor in the User Defined Runtime Attributes to add a border color to any, UIView.
Łukasz-kalbarczykenter
http://stackoverflow.com/a/46215487/1058199
It traps the setting of each runtime attribute and traps for the key name of borderColor. If we have a match, it returns the cgColor value for
borderColor from the supplied UIColor.
Here is my updated answer from the work of Łukasz-kalbarczykenter
All I changed was set the extension to CALayer from UIView and made sure to use self.border
import UIKit
extension CALayer {
open override func setValue(_ value: Any?, forKey key: String) {
guard key == "borderColor", let color = value as? UIColor else {
super.setValue(value, forKey: key)
return
}
self.borderColor = color.cgColor
}
}
Next, in the storyboard, just select your UIView, press command option 4 and just add layer.borderColor as your desired UIColor to the User Defined Runtime Attributes. Then run your app and it should work!

you should use : layer.borderColor and set layer.masksToBounds = false

This will work for sure.
you can manage this through storyborad as you are doing but you are passing the wrong key here. it should be
layer.borderColorFromUIColor

Related

Border color doesn't change when changing themes in iOS 13

I recently developed app which is compatible with Dark mode.
And dark mode also works fine.
Btw when I change from dark->light, light->dark mode from device, all colors change as expected except border color.
Let's say border color is black when light mode and white when dark mode and system setting is dark mdoe.
When I change system setting to light mode and return to app, all border colors stay white which is supposed be black.
Has anyone ever faced this issue and could you please help me solve this problem?
This is serious problem when I want to implement real-time theme update in app.
Thanks.
Thanks to #KurtRevis, I finally managed to solve the problem.
You need to listen to traitCollectionDidChange. If you want to change borderColor when appearance changes, you need code something like this.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if #available(iOS 13.0, *) {
if (traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection)) {
// ColorUtils.loadCGColorFromAsset returns cgcolor for color name
layer.borderColor = ColorUtils.loadCGColorFromAsset(colorName: "CellBorderColor")
}
}
}
Hope this helps others
Depending on your circumstance you could make use of UIColor(dynamicProvider:), especially if traitCollectionDidChange(_:) is not an option (e.g. in a UIButton extension). Whenever the dynamicProvider is called upon to deliver the title color, it gives us an opportunity to update our border color as well. The example below just updates the border color to match the title color, but any other color could be used.
extension UIButton {
func applyLoginStyle() {
let titleColorProvider = UIColor { [weak layer] traitCollection in
let titleColor = UIColor(named: "myColor")!.resolvedColor(with: traitCollection)
layer?.borderColor = titleColor.cgColor
return titleColor
}
setTitleColor(titleColorProvider, for: .normal)
}
}

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

Can you add IBDesignable properties to UIView using categories/extensions?

For those that don't know what I'm talking about, Xcode 6.0 added new features, IBDesignable and IBInspectable.
When you tag your custom views with IBInspectable properties, those properties show up in the Attributes Inspector in IB.
Likewise, when you tag a custom UIView subclass with IBDesignable, Xcode compiles your views and invokes the code to render your view objects right in the Xcode window so you can see what they look like.
The technique for adding IBDesignable and IBInspectable attributes to custom views is pretty much identical in Swift and Objective-C. IBInspectable properties appear in the Interface Builder Attributes Inspector regardless of which language you use to define them.
I've created a category of UIView in Objective-C and an extension of UIView in Swift that promote the borderWidth, cornerRadius, borderColor, and layerBackgroundColor properties of the view's underlying layer as properties of the view. If you change the property, the extension/category does type conversion as required and forwards the change to the layer.
The IBInspectable part works great. I see and can set the new properties in the IB attributes inspector.
I could have sworn that last week, the IBDesignable attribute on my view category/extension was working too, and I could see my custom UIView category rendering in IB with it's changed layer attributes. This week it isn't working.
Was I hallucinating?
Can categories/extensions of existing system classes draw their custom UI in Interface Builder when they are set up with IBDesignable?
Since posting this question I've learned that #IBDesignable does not work for class extensions. You can add the tag, but it has no effect.
I was able to make it work with code below, but the side effect is that some times IB agent in storyboard crashes because it has to refresh too many UI elements. Restarting Xcode fixes problem temporarily until next crash. Maybe that's the problem OP is facing
#IBDesignable
extension UIView
{
#IBInspectable
public var cornerRadius: CGFloat
{
set (radius) {
self.layer.cornerRadius = radius
self.layer.masksToBounds = radius > 0
}
get {
return self.layer.cornerRadius
}
}
#IBInspectable
public var borderWidth: CGFloat
{
set (borderWidth) {
self.layer.borderWidth = borderWidth
}
get {
return self.layer.borderWidth
}
}
#IBInspectable
public var borderColor:UIColor?
{
set (color) {
self.layer.borderColor = color?.cgColor
}
get {
if let color = self.layer.borderColor
{
return UIColor(cgColor: color)
} else {
return nil
}
}
}
}
That's why I am trying to add where clause to reduce subclasses which should extend this functionality:
Generic IBDesginables UIView extension
#IBDesignable work with UIView extension only in custom class.For example. UILabel is a default sub-class of UIView. It won't work there, but if you make a custom class called MyUILabel subclassing UILabel. assign the MyUILabel class to the Label your are working on. Then your corner radius in UIView extension will work of this MyUILabel.
( I guess the first week it work for you is because you are dealing with some custom class.)
I've made this work for my use case by having one #IBDesignable UIView that I set as the top view in my view controller. My particular use case is making ClassyKit styling visible in Interface Builder on the default UIKit views without have to subclass just for that and it's working great.
Here's an example of how you could set it up:
// in Interface Builder set the class of your top view controller view to this
#IBDesignable class DesignableView: UIView {
}
extension UIView {
open override func prepareForInterfaceBuilder() {
subviews.forEach {
$0.prepareForInterfaceBuilder()
}
}
}
extension UILabel {
// just an example of doing something
open override func prepareForInterfaceBuilder() {
layer.cornerRadius = 8
layer.masksToBounds = true
backgroundColor = .red
textColor = .green
}
}
This code block is working well for me.
import UIKit
public extension UIView {
#IBInspectable public var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
}
NOTE It might not work when being imported from a framework. I am trying to find out the reason now.

Bordered iOS 8 Buttons Swift

I want a flat looking white bordered around my UIButton. I would like it in Storyboard or programmatically. My code isn't working.
Code:
UIButton.layer.cornerRadius = 2;
UIButton.layer.borderWidth = 1;
UIButton.layer.borderColor = UIColor.whiteColor()
You should create referencing outlet for your button from storyboard to your VC named for example myButton than set its properties
myButton.layer.cornerRadius = 2;
myButton.layer.borderWidth = 1;
myButton.layer.borderColor = UIColor.whiteColor().CGColor
You don't have to do this with code either. You can create a stretchable image and set it to the background image of the button in the attributes inspector.
as 0x7fffffff said. UIButton is the class it can be instatiated bu invoking its constructor like this
let instanceOfUIButton = UIButton()
then you can set the desired attributes:
instanceOfUIButton.layer.cornerRadius = 2;
In Xcode 8.2 (Swift 3) you can use "Identity Inspector Tab". Search for "Users Defined Runtime Attributes", after selecting your UIButton. There you can define these attributes:
Key Path: layer.cornerRadius; 2-Type: Number, 3-Value: 2
Key Path: layer.borderWidth; 2-Type: Number, 3-Value: 1
Key Path: layer.borderColor; 2-Type: Color, 3-Value: "select white or another one"
Another option instead of creating a reference to each button would be to create a subclass of the type UIButton. You could then set the properties in the subclass. Next you could change the class of all the buttons in the storyboard that need to have the same properties.
class MyButton: UIButton {
override func draw(_ rect: CGRect) {
super.draw(rect)
layer.borderWidth = 1.0
layer.borderColor = UIColor.White
layer.cornerRadius = 2
}
}
Add this line at the top
myButton.layer.masksToBounds = true

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

Resources