|
// Rounded opaque buttons for iOS |
|
// Drop-in replacement for UIButton with Interface Builder support |
|
// UIButton.buttonType type should be set to .custom |
|
// Color is chosen based on the tint color. Supports highlighted and selected states |
|
// Assumes that tint color has high brightness and saturation (e.g. not white or black) |
|
// Written for iOS 12 with Swift 4.2 |
|
|
|
import UIKit |
|
|
|
let kRoundedButtonDefaultFont = UIFont.systemFont(ofSize: 14.0, weight: .medium) |
|
let kRoundedButtonDefaultCornerRadius: CGFloat = 4.0 |
|
let kRoundedButtonDefaultVerticalMargin: CGFloat = 6.0 |
|
let kRoundedButtonDefaultHorizontalMargin: CGFloat = 12.0 |
|
|
|
@IBDesignable class RoundedButton: UIButton { |
|
@IBInspectable var highlightDimmingFactor: CGFloat = 0.85 { |
|
didSet { updateColors() } |
|
} |
|
@IBInspectable var backgroundSaturationFactor: CGFloat = 0.1 { |
|
didSet { updateColors() } |
|
} |
|
|
|
override init(frame: CGRect) { |
|
super.init(frame: frame) |
|
setDefaultValues() |
|
} |
|
|
|
required init?(coder aDecoder: NSCoder) { |
|
super.init(coder: aDecoder) |
|
setDefaultValues() |
|
} |
|
|
|
func setDefaultValues() { |
|
titleLabel?.font = kRoundedButtonDefaultFont |
|
cornerRadius = kRoundedButtonDefaultCornerRadius |
|
verticalMargin = kRoundedButtonDefaultVerticalMargin |
|
horizontalMargin = kRoundedButtonDefaultHorizontalMargin |
|
|
|
updateColors() |
|
} |
|
|
|
@IBInspectable var cornerRadius: CGFloat { |
|
get { return layer.cornerRadius } |
|
set { |
|
layer.cornerRadius = newValue |
|
layer.masksToBounds = newValue > 0 |
|
} |
|
} |
|
|
|
@IBInspectable var verticalMargin: CGFloat { |
|
get { return contentEdgeInsets.top } |
|
set { |
|
contentEdgeInsets.top = newValue |
|
contentEdgeInsets.bottom = newValue |
|
} |
|
} |
|
|
|
@IBInspectable var horizontalMargin: CGFloat { |
|
get { return contentEdgeInsets.left } |
|
set { |
|
contentEdgeInsets.left = newValue |
|
contentEdgeInsets.right = newValue |
|
} |
|
} |
|
|
|
func updateColors() { |
|
let primaryColor = tintColor! |
|
let desaturatedColor = tintColor.multipliedHSBColor(1.0, backgroundSaturationFactor, 1.0) |
|
|
|
let customTextColor = isSelected ? .white : primaryColor |
|
setTitleColor(customTextColor, for: .normal) |
|
setTitleColor(customTextColor.multipliedHSBColor(1.0, 1.0, highlightDimmingFactor), for: .highlighted) |
|
|
|
let currentDimmingFactor = isHighlighted ? highlightDimmingFactor : 1.0 |
|
let customBackgroundColor = isSelected ? primaryColor : desaturatedColor |
|
|
|
backgroundColor = customBackgroundColor.multipliedHSBColor(1.0, 1.0, currentDimmingFactor) |
|
} |
|
|
|
override var isHighlighted: Bool { |
|
didSet { updateColors() } |
|
} |
|
|
|
override var isSelected: Bool { |
|
didSet { updateColors() } |
|
} |
|
|
|
override func tintColorDidChange() { |
|
super.tintColorDidChange() |
|
updateColors() |
|
} |
|
} |
|
|
|
extension UIColor { |
|
func multipliedHSBColor(_ hueFactor: CGFloat, _ saturationFactor: CGFloat, _ brightnessFactor: CGFloat) -> UIColor { |
|
var h: CGFloat = 0.0 |
|
var s: CGFloat = 0.0 |
|
var b: CGFloat = 0.0 |
|
var a: CGFloat = 0.0 |
|
getHue(&h, saturation: &s, brightness: &b, alpha: &a) |
|
h = clamp(h * hueFactor, in: 0.0...1.0) |
|
s = clamp(s * saturationFactor, in: 0.0...1.0) |
|
b = clamp(b * brightnessFactor, in: 0.0...1.0) |
|
return UIColor(hue: h, saturation: s, brightness: b, alpha: a) |
|
} |
|
} |
|
|
|
public func clamp<T>(_ value: T, in range: ClosedRange<T>) -> T where T : Comparable { |
|
return min(max(value, range.lowerBound), range.upperBound) |
|
} |