Custom UIbutton default color Xcode Swift - ios

I'm trying to create a custom UIButton class like so, but I don't see the backGround color updated, also it doesn't show as the default color in the inspector either. I want it to be so that I can change all the colors of those instances from this class file without having to set it manually for each button.
import UIKit
#IBDesignable class PrimaryButton: UIButton {
#IBInspectable var borderwidth: CGFloat = 0.0 {
didSet {
self.layer.borderWidth = borderwidth
}
}
#IBInspectable var cornerradius: CGFloat = 4.0 {
didSet {
self.layer.cornerRadius = cornerradius
}}
}
#IBInspectable var backgroundcolor: UIColor = UIColor.red {
didSet {
self.layer.backgroundColor = backgroundcolor.cgColor
}
}
}

You can try to create custom class like this then set it for any button in IB
#IBDesignable class MyNewButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
shared()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
shared()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
shared()
}
func shared() {
//TODO: add any custom settings here
self.layer.cornerRadius = value
self.layer.borderWidth = value
self.layer.borderColor = UIColor.red.cgColor
}
}

Related

Round Corners in custom UICollectionViewCell not working in Swift

I have custom UICollectionViewCell and try to round corners for button, which doesn't work.
I had the same problem for ViewController and the problem was that I was doing rounding in viewDidLoad instead of subviewsDidLoad.
I had no idea what is a problem now.
sharing my code.
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
initialsButton.layer.cornerRadius = 0.5 * initialsButton.frame.size.width
initialsButton.clipsToBounds = true
initialsButton.layer.masksToBounds = true
}
-> but I also tried without .clipsToBounds and .masksToBounds. Same result.
Here is the result. It is not a circle, it makes corner wrongly
SEE THIS RESULT
The problem was that I was doing rounding corners in awakeFromNib(), instead you MUST do it in layoutSubviews() and it works nicely.
I suppose you are using IBOutlet's
Create a class something like this, add button to your cell in storyboard, edit there how ever you want, it easier. Your code up is not good, just remove size, frame.height / 2 is what you want if you are going that way.
#IBDesignable
class RoundedBtn: UIButton {
#IBInspectable var cornerRadius: Double {
get {
return Double(self.layer.cornerRadius)
}set {
self.layer.cornerRadius = CGFloat(newValue)
}
}
#IBInspectable var borderWidth: Double {
get {
return Double(self.layer.borderWidth)
}
set {
self.layer.borderWidth = CGFloat(newValue)
}
}
#IBInspectable var borderColor: UIColor? {
get {
return UIColor(cgColor: self.layer.borderColor!)
}
set {
self.layer.borderColor = newValue?.cgColor
}
}
#IBInspectable var shadowColor: UIColor? {
get {
return UIColor(cgColor: self.layer.shadowColor!)
}
set {
self.layer.shadowColor = newValue?.cgColor
}
}
#IBInspectable var shadowOffSet: CGSize {
get {
return self.layer.shadowOffset
}
set {
self.layer.shadowOffset = newValue
}
}
#IBInspectable var shadowRadius: Double {
get {
return Double(self.layer.shadowRadius)
}set {
self.layer.shadowRadius = CGFloat(newValue)
}
}
#IBInspectable var shadowOpacity: Float {
get {
return self.layer.shadowOpacity
}
set {
self.layer.shadowOpacity = newValue
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
self.titleLabel?.numberOfLines = 0
self.titleLabel?.textAlignment = .center
self.setContentHuggingPriority(UILayoutPriority.defaultLow + 1, for: .vertical)
self.setContentHuggingPriority(UILayoutPriority.defaultLow + 1, for: .horizontal)
}
override var intrinsicContentSize: CGSize {
let size = self.titleLabel!.intrinsicContentSize
return CGSize(width: size.width + contentEdgeInsets.left + contentEdgeInsets.right, height: size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
}
}

Unable to remove a CALayer from UITextField

So, I have this custom UITextField and I have two methods to add CALayer and remove the CALayer but remove is not working.
#IBDesignable class AppTextField : UITextField {
private let bottomLine = CALayer()
override func layoutSubviews() {
self.font = .systemFont(ofSize: 20)
self.addBottomLine()
self.clearButtonMode = .unlessEditing
super.layoutSubviews()
}
func removeBttomLine() {
bottomLine.removeFromSuperlayer()
}
private func addBottomLine() {
bottomLine.frame = CGRect(origin: CGPoint(x: 0, y: self.frame.height + 4), size: CGSize(width: self.frame.width, height: 1))
bottomLine.backgroundColor = UIColor.init(hexString: "#DCCFCA")?.cgColor
self.borderStyle = .none
self.layer.addSublayer(bottomLine)
}
}
The only thing you should do in layoutSubviews() is update frames as necessary.
This will show the red line when the field is NOT being edited, and will remove it while the field IS being edited:
#IBDesignable class AppTextField : UITextField {
private let bottomLine = CALayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
self.font = .systemFont(ofSize: 20)
self.backgroundColor = .white
self.clearButtonMode = .unlessEditing
self.borderStyle = .none
bottomLine.backgroundColor = UIColor.red.cgColor
addBottomLine()
}
override func layoutSubviews() {
super.layoutSubviews()
var r = bounds
r.origin.y = bounds.maxY
r.size.height = 4.0
bottomLine.frame = r
}
func removeBottomLine() {
bottomLine.removeFromSuperlayer()
}
private func addBottomLine() {
self.layer.addSublayer(bottomLine)
}
override func resignFirstResponder() -> Bool {
super.resignFirstResponder()
addBottomLine()
return true
}
override func becomeFirstResponder() -> Bool {
super.becomeFirstResponder()
removeBottomLine()
return true
}
}
The reason could because the layoutSubviews method gets called multiple times and the layer is getting added multiple times. Try moving the addBottomLine method to required init?(coder:) method if you're using storyboard or use init(frame:) or custom init whichever gets called just once. Here's an example:
#IBDesignable class AppTextField : UITextField {
required init?(coder: NSCoder) {
super.init(coder: coder)
addBottomLine()
}
}
You're doing 3 things in your add method:
adusting frame
setting color
adding as sublayer
And because you're calling it from layoutSubviews it's called multiple times and you're ending with multiple layers added and that's why calling remove doesn't seem to work.
To make this code work you should move adding part to init (withCoder, withFrame or both). You can join it with setting color because it can be done once. Next part is adjusting frame in layoutSubviews which is required because layers can't into autolayout. At the end you will have creation, adding as sublayer and set part called once at init, and adjusting called multiple times on layout pass. Now remove when called once - it would work with visible effect this time.

Swift UIButton Subclass and change color based on variable

I'm using a subclass for my UIButton and it has a variable called isActive. I need to change the button border color based on that variable. This variable will change programmatically. Please help me with this.
#IBDesignable
class buttonCTAOutlineDark: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override func prepareForInterfaceBuilder() {
commonInit()
}
#IBInspectable var isActive: Bool {
get {
return self.isActive
}
set (active) {
if active {
commonInit(isActive: active)
}
}
}
func commonInit(isActive: Bool = false) {
self.backgroundColor = .clear
self.layer.cornerRadius = 4
self.layer.borderWidth = 1
if (isActive) {
self.tintColor = ACTIVE_COLOR
self.layer.borderColor = ACTIVE_COLOR.cgColor
} else {
self.tintColor = nil
self.layer.borderColor = UIColor(red:0.69, green:0.72, blue:0.77, alpha:1.0).cgColor
}
}
}
You should be observing didSet to update the view. In Swift, type names should start with Uppercase e.g ButtonCTAOutlineDark. Please see the fixed class,
#IBDesignable
class ButtonCTAOutlineDark: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
#IBInspectable var isActive: Bool = false {
didSet {
self.commonInit(isActive: self.isActive)
}
}
func commonInit(isActive: Bool = false) {
self.backgroundColor = .clear
self.layer.cornerRadius = 4
self.layer.borderWidth = 1
if (isActive) {
self.tintColor = ACTIVE_COLOR
self.layer.borderColor = ACTIVE_COLOR.cgColor
} else {
self.tintColor = nil
self.layer.borderColor = UIColor(red:0.69, green:0.72, blue:0.77, alpha:1.0).cgColor
}
}
}
Your isActive property is written incorrectly. It should not be a computed property in the first place. Currently, the getter will just cause infinite recursion and the setter doesn't actually set anything.
The isActive property should be a stored property with a didSet property observer:
#IBInspectable
var isActive: Bool {
didSet {
}
}
Inside didSet, you can just put the last part of commonInit. The first part of commonInit doesn't need to be run every time isActive changes. I recommend you to extract that as a method called updateBorder:
func updateBorder(isActive: Bool) {
if (isActive) {
self.tintColor = ACTIVE_COLOR
self.layer.borderColor = ACTIVE_COLOR.cgColor
} else {
self.tintColor = nil
self.layer.borderColor = UIColor(red:0.69, green:0.72, blue:0.77, alpha:1.0).cgColor
}
}
And then in didSet, you can just call that:
updateBorder(isActive: isActive)

Updating context fill color on didSet

I made a custom UIView which is basically a colored circle. However, when I change the circle's color property, it's not updating. How can I do this?
Code
class CircleView : UIView {
var color = UIColor.blue {
didSet {
// WHAT DO I DO!?
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {return}
context.addEllipse(in: rect)
context.setFillColor(color.cgColor)
context.fillPath()
}
}
Try the following one
var color = UIColor.blue {
didSet {
setNeedsDisplay()
}
}

UIButton backgroundRect(forBounds:) not being called

I have a custom hexagon shaped button and I want to disable the background color so it wont create a square shape around the hexagon itself.
I am using the backgroundRect(forBounds:) method to override the background's rect.
Here is the full implementation:
class HexaButton : UIButton {
var shape : CAShapeLayer!
func setup() {
if shape == nil {
shape = CAShapeLayer()
shape.fillColor = self.backgroundColor?.cgColor
shape.strokeColor = UIColor.cyan.cgColor
shape.lineWidth = 5.0
self.backgroundColor = UIColor.yellow
self.layer.addSublayer(shape)
}
let side : CGFloat = 6
let r = frame.height/side
shape.path = ShapeDrawer.polygonPath(x: frame.width/2, y: frame.height/2, radius: r, sides: Int(side), pointyness: side/2.0)
}
override func backgroundRect(forBounds bounds: CGRect) -> CGRect {
return .zero
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if shape?.path?.contains(point) == true {
return self
} else {
return nil
}
}
}
For some unknown reason, the backgroundRect(forBounds:) isn't being called.
Any Ideas?
You need to set the backgroundImage property of your UIButton and backgroundRect method will be called.

Resources