Reputation: 5004
I am still improving my programing skills in iOS(Swift). Today I am thinking about creating a UIView subclasses in a good way. I want to create custom view which is:
I know such methods as init(withFrame)
init()
init(withDecoder)
prepareForInterfacebuilder()
, etc but I don't know how to use them to minimize redundancy of code.
For example here is my custom UIButton
:
import UIKit
@IBDesignable
class SecurityButton: UIButton {
@IBInspectable
var color: UIColor = UIColor.black {
didSet {
setTitleColor(color, for: .normal)
contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0)
layer.borderWidth = 1.0
layer.borderColor = color.cgColor
layer.cornerRadius = 6.0
}
}
}
It works good but I know that contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0)
layer.cornerRadius = 6.0
layer.borderWidth = 1.0
are called every I set a new color. Where should I place this custom setup to keep my requirements about custom view?
I do not looking for fix my example. I looking for a good place to initialize my custom view. Suppose that you need to do some time-consuming operations but only ones on create view.
Upvotes: 2
Views: 3971
Reputation: 77690
You can create a "custom init" function to handle the setup tasks.
So, for example:
import UIKit
@IBDesignable
class SecurityButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
func commonInit() {
layer.borderWidth = 1.0
layer.borderColor = color.cgColor
layer.cornerRadius = 6.0
_contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 8.0)
updateEdgeInsets()
}
private var _contentEdgeInsets: UIEdgeInsets = UIEdgeInsets.zero {
didSet { updateEdgeInsets() }
}
override public var contentEdgeInsets: UIEdgeInsets {
get { return super.contentEdgeInsets }
set { _contentEdgeInsets = newValue }
}
private func updateEdgeInsets() {
let initialEdgeInsets = contentEdgeInsets
let t = _contentEdgeInsets.top + initialEdgeInsets.top
let l = _contentEdgeInsets.left + initialEdgeInsets.left
let b = _contentEdgeInsets.bottom + initialEdgeInsets.bottom
let r = _contentEdgeInsets.right + initialEdgeInsets.right
super.contentEdgeInsets = UIEdgeInsets(top: t, left: l, bottom: b, right: r)
}
@IBInspectable
var color: UIColor = UIColor.black {
didSet {
setTitleColor(color, for: .normal)
}
}
}
Edit: Custom contentEdgeInsets
have to be handled a little differently than most other properties.
@KamilHarasimowicz - Your "question edit" states "I do not looking for fix my example. I looking for a good place to initialize my custom view." ...
My initial answer did exactly that - it showed you where to initialize your custom view.
However, since your comment "storyboard don't render contentEdgeInsets in your solution" indicates that you actually DO want your example fixed (otherwise you would have just fixed it yourself), I have edited my answer to do so.
Upvotes: 3
Reputation: 413
You are missing the function
self.setNeedsDisplay()
Your code should look as follows
import UIKit
@IBDesignable
class SecurityButton: UIButton {
@IBInspectable
var color: UIColor = UIColor.black {
didSet {
setTitleColor(color, for: .normal)
contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0)
layer.borderWidth = 1.0
layer.borderColor = color.cgColor
layer.cornerRadius = 6.0
self.setNeedsDisplay()
}
}
}
This will call the draw function that will update the storyboard view. The function should be called as last in the didSet Now it will show up after you set the color.
I tested it and It works for me in xcode, after I set the value color in the storyboard.
If you want it to draw always not only when u set a color by the inspector you can use the draw(rect) function it will look following
import UIKit
@IBDesignable
class SecurityButton: UIButton {
@IBInspectable
var color: UIColor = UIColor.black
override func draw(_ rect: CGRect) {
setTitleColor(color, for: .normal)
contentEdgeInsets = UIEdgeInsetsMake(0.0, 8.0, 0.0, 8.0)
layer.borderWidth = 1.0
layer.borderColor = color.cgColor
layer.cornerRadius = 6.0
setNeedsDisplay()
}
}
This will draw the border always, not only when you set in in the interface inspector.
Upvotes: 2