I have a subclass of UIView called MyView that I am creating.
In it there is a UILabel (this is added in code not in InterfaceBuilder for reasons).
I then have a property on MyView called color.
What I'd like is to have Interface Builder be able to select the color and also to then display the label with the font set to that color.
In my code I have...
#IBDesignable class MyView: UIView {
private let textLabel = UILabel()
#IBInspectable var color: UIColor = .blackColor() {
didSet {
textLabel.textColor = color
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialSetup()
}
override init(frame: CGRect) {
super.init(frame: frame)
initialSetup()
}
private func initialSetup() -> Void {
self.backgroundColor = .clearColor()
textLabel.textAlignment = .Center
textLabel.numberOfLines = 0
addSubview(textLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
textLabel.frame = bounds
}
// I have no idea what I'm doing here?!
override func prepareForInterfaceBuilder() {
// should I create a label specifically for here?
// or should I use the property textLabel?
textLabel.text = "Hello, world!" // setting place holder IB text?
textLabel.textColor = color
textLabel.font = .systemFontOfSize(21)
textLabel.textAlignment = .Center
textLabel.numberOfLines = 0
// do I need to add sub view?
}
}
IBInspectable
In IB I can see the color property there and I can set it. Annoyingly it takes a default value of .clearColor() though not the color I set in code. Is there a way to fix that?
IBDesignable
When I put the view into InterfaceBuilder it shows all the inspectable properties but nothing is shown in the actual view.
When I run it it all works fine. I'd just like to be able to get something working (other than drawRect) in IB. I'm finding a distinct lack of documentation though.
Edit - Warnings
I just noticed that I'm getting build errors / warnings saying...
warning: IB Designables: Ignoring user defined runtime attribute for key path "color" on instance of "UIView". Hit an exception when attempting to set its value: [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key color.
This will probably change the solution :)
I copied the code from your question into a new app, added the view and set the property and it worked just fine.
The error you're seeing suggests that at some point you've changed the view back to a plain UIView in the identity inspector (the user defined attributes remain in that case).
When color is set you need to update the textLabel property like below using a didSet
#IBInspectable var color: UIColor = UIColor.blackColor() {
didSet {
textLabel.textColor = color
}
}
I am using swift 2.0 and in my case was missing dynamic property. Final code:
#IBInspectable dynamic var radius:CGFloat = 2.0 {
didSet {
// setNeedsDisplay()
layer.cornerRadius = radius
}
}
Find out error during build for IB Designables: http://lamb-mei.com/537/xcode-ibdesignables-ignoring-user-defined-runtime-attribute-for-key-path-bordercolor-on-instance-of-uiview-this-class-is-not-key-value-coding-compliant-for-the-key-bordercolor/
Related
Is there a way to change and save the default values of the attributes so they are used for all new objects that are created in the future?
Example:
Once I create and style a button or textfield for a project, I would like all additional buttons to have the same style. Instead, every new button, text box, etc created has Xcode's default properties. Having to set the attributes for every new object is exhausting. Is there a way to save a created object as the "new" default of that object type...at least for the current project.
I have tried using the IBDesignables and IBInspectable but that simply creates new attributes. In fact the new attributes don't even display the default values assigned when viewing the attributes inspector. When changing an existing property in the inspector, such as backgrounds or borders, it creates a totally separate attribute and then it is confusing which attribute overrides which.
Another example:
I would like textfields set to a certain size with a certain font, size and color, borders, rounded corners, etc. This would be the default but on my story boards, I may wish to change the width or other properties not set in the defaults.
I have tried to search using hundreds of different terms but nothing matches what I am asking. It can't be that difficult???? Yes, I am a bit of a nub when it comes to Xcode and swift. All the other coding tools I can think of have a provision for setting defaults when creating new objects.
What you're looking to do is called Subclasssing. You make a new class that inherits from the class you want to extend, and then do your custom settings on it. You can use your subclass in Storyboard by setting the custom class in the Identity Inspector, if you want to be able to set properties from Storyboard then you can add them via #IBInspectable and have that property set the actual property. If you don't need to see the custom settings in storyboard then it's easier to just set the value in views initialization.
#IBDesignable
class MyTextField: UITextField {
#IBInspectable
var cornerRadius: CGFloat {
set { self.layer.cornerRadius = newValue }
get { self.layer.cornerRadius }
}
#IBInspectable
var borderColor: UIColor? {
set { super.layer.borderColor = newValue?.cgColor }
get { UIColor(cgColor: self.layer.borderColor ?? UIColor.black.cgColor) }
}
override var borderStyle: UITextField.BorderStyle {
set { super.borderStyle = .none }
get { .none }
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
func setupView() {
self.font = UIFont(name: "Courier", size: 12)
self.textAlignment = .center
self.placeholder = "My custom placeholder text"
self.layer.borderWidth = 3
}
}
I found a similar question here. But the problem is it is in OBJ-C. I do not know the code and am working in SWIFT so please can anyone explain and translate this code in swift.
I am still new to swift so please help me.
Regards
Create
var globalColor = UIColor.red
class CustomLbl:UILabel {
override func awakeFromNib() { // inside IB
super.awakeFromNib()
self.textColor = globalColor
}
override func draw(_ rect: CGRect) { // programmatically
super.draw(rect)
self.textColor = globalColor
}
}
And assign it in IB or code , for programmatically call
self.view.setNeedsDisplay()
after you change global color
with an extension. Maybe its not the perfect solution but once you applied it to all needed labels, then it applies to all labels when you make a change. Create a new swift file and put the following code in:
import UIKit
extension UILabel {
func labelColor() {
self.textColor = UIColor.red //or whichever color you want
}
}
And in the viewDidLoad or viewWillLoad you can do:
yourLabel.labelColor()
self.view.subviews.forEach { (view) in // Loop
if let label = view as? UILabel { // check the type
label.textColor = .red // assign the color
}
}
And self is your viewController
As explain in the similar question, you have to
Loop through all the UIView's in self.view.subviews and check if it's
of type UILabel.
I've created a subclass to manage my Theme but is not showing neither on device or simulator.
Here my Header.swift:
import Foundation
import UIKit
class Header: UILabel {
override var textColor: UIColor! {
// White Color
get { return ThemeManager.currentTheme.palette.primary }
set {}
}
override var font: UIFont! {
get { return ThemeManager.currentTheme.textStyle.headerText }
set {}
}
}
Here the implementation: (inside the viewcontroller)
var titleLabel: Header = Header()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
// Background Image over the view
setupBackground()
setupStartButton()
setupTitleLabel()
print(titleLabel.frame)
}
// MARK: - Header
private func setupTitleLabel() {
titleLabel.text = "0.0m"
// titleLabel.font = ThemeManager.currentTheme.textStyle.headerText
// titleLabel.textColor = ThemeManager.currentTheme.palette.primary
view.addSubview(titleLabel)
view.bringSubviewToFront(titleLabel)
setupTitleLabelAutolayout()
}
private func setupTitleLabelAutolayout() {
titleLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
But if I use UILabel instead of Header it works perfectly as expected.
I've also tried to implement init?(coder aDecoder: NSCoder) and init(frame: CGRect) but nothing changed.
If I set a frame on init then shows the text, but not styled and ignoring my constraints.
Surely I'm missing something, but what?
To avoid usefulness answers, here some infos:
The UILabel textColor is white
The background is black and has an image over it.
I've tried to remove the image and all the stuff around except for the label and nothing changed.
That's a poor reason to use subclassing. It doesn't allow you to mix-and-match when appropriate.
Better would be to make an extension:
extension UILabel {
func withHeaderStyle() -> UILabel {
self.textColor = ThemeManager.currentTheme.palette.primary
self.font = ThemeManager.currentTheme.textStyle.headerText
return self
}
}
Then at point of use:
var titleLabel = UILabel().withHeaderStyle()
You can make several of these "withXStyle" methods and at the point of use you can chain them together. That's something you can't do with inheritance.
In general you should only use inherence when you want to change behavior. It's ill suited for changing data.
I've fixed that by editing the Header to this:
import Foundation
import UIKit
class Header: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
setupStyle()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupStyle()
}
private func setupStyle() {
self.textColor = ThemeManager.currentTheme.palette.primary
self.font = ThemeManager.currentTheme.textStyle.headerText
}
}
Basically if I understood right, when I set the getter in the label it doesn't (if you think about, it's quite obvious) anything.
I still think that there are better solutions, but this works fine for me so, I'll keep that.
Now you may ask: "Why did you overwritten the getter instead of doing this?"
It's the right question, and the right answer is that I read it in a swift article on medium, so I tought it was right.
PS: I've also tried with didSet but it obviously loop through it self and crash.
I want to set the default text color for all UILabels in my app. I found this:
UILabel.appearance().textColor = UIColor.red
This works, but when I want to set some other color for a particular label in its storyboard, it cannot be done - all labels are still red.
Is it possible to define a default text color for all UILabels by setting it in storyboards and then change it for some UILabels ALSO in storyboards (not in code)? Or set the default color in code using the Appearance API and change it for some labels in storyboard?
SubClass you labels with CustomLabel.swift. You can set text color using IBDesignable property named as txtColor
Below is the code working example
import UIKit
#IBDesignable class CustomLabel: UILabel {
#IBInspectable var txtColor: UIColor = UIColor.grayColor() {
didSet {
self.textColor = txtColor
}
}
func setup() {
self.textColor = txtColor
}
override func awakeFromNib() {
setup()
}
override func prepareForInterfaceBuilder() {
setup()
}
}
There is also an alternative to what Muhammad suggested.
The alternative is to use the UIAppearance proxy, but specify the classes where it would be applied to.
In that case, you would use it like this:
UILabel.appearanceWhenContainedInInstancesOfClasses([MyViewController.self, MyotherViewController.self]).textColor = UIColor.red
The above though, would only allow you to set the textColor your self in some classes. in those where the proxy is set to be enabled, you won't be able to set the text color.
It's up to you to choose which approach is better for your case
I am new on iOS and Swift and I need some help.
I want create custom UIButton
Here is what I did
protocol ButtonProtocol {}
extension ButtonProtocol where Self: UIButton {
func addOrangeButton(){
layer.cornerRadius = 8
layer.backgroundColor = UIColor(netHex:ButtonColor.orange).cgColor
}
}
I want all params came from here which are cornerRadius, backgrounColor, highlightedColor, textColor, size etc...
I want use this way bcoz maybe in future the button color will change I will change it from one place directly.
But I don't understand what is layer how could I cast it as UIButton?
Is anyone can tell me which way should I take ?
You can create the subclass of UIButton, to add your own custom look to your button. like this
import UIKit
protocol DVButtonCustomMethods: class {
func customize()
}
class DVButton: UIButton {
var indexPath: IndexPath?
override init(frame: CGRect) {
super.init(frame: frame)
customize()// To set the button color and text size
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
customize()// To set the button color and text size
}
override func layoutSubviews() {
super.layoutSubviews()
customize()
}
}
extension DVButton: DVButtonCustomMethods {
func customize() {
layer.cornerRadius = self.frame.size.height / 2
backgroundColor = UIColor.white
tintColor = UIColor.red
titleLabel?.textColor = UIColor.black
clipsToBounds = true
}
}
Now what is need to do is, create one button in interface builder and assign you subClass as its class. Thats all everything will change as you want. If you want to change button colour just change in your subclass, it will affect in all button which is assigned your subclass.
Assigning subclass to your button: Refer below image
Thanks:)
The way you defined the extension, doesn't make you able to use it in the UIButton instance so simple.
So, you can decide whether extend UIButton to conform the protocol, or you can create a subclass of UIButton
// in this way you can use the `addOrangeButton` method anywhere
extension UIButton: ButtonProtocol {}
// in this way your new subclass contains the addOrangeButton definition
// and a normal UIButton cannot access that method
final class OrangeButton: UIButton, ButtonProtocol {
func setupButton() {
addOrangeButton()
}
}
Try this:
class func CutomeButton(bgColor: UIColor,corRadius: Float,hgColor: UIColor, textColor: UIColor, size: CGSize, titleText: String) -> UIButton {
let button = UIButton()
button.layer.cornerRadius = CGFloat(corRadius)
button.backgroundColor = bgColor
button.setTitleColor(textColor, for: .normal)
button.frame.size = size
button.setTitle(titleText, for: .normal)
return button
}
If I understand well, you want to modify a UIButton with specific parameters, let me tell you how do I do this:
extension UIButton
{
func setRadius(radius:CGFloat) {
self.layer.cornerRadius = radius
}
}
Use it as the following:
yourButton.setRadius(radius: 15)