Skip to content

Instantly share code, notes, and snippets.

@Marcocanc
Last active September 3, 2023 09:14
Show Gist options
  • Save Marcocanc/157dae2ae5976de28b3e3020fdff01d5 to your computer and use it in GitHub Desktop.
Save Marcocanc/157dae2ae5976de28b3e3020fdff01d5 to your computer and use it in GitHub Desktop.
A UILabel subclass that can hold attributes and apply them to text
import UIKit
/// A UILabel subclass that can hold attributes and apply them to text
@IBDesignable
final class Label: UILabel {
convenience init(attributes: [NSAttributedStringKey: Any]) {
self.init()
self.attributes = attributes
}
// MARK: Properties
/// The attributes for the attributed string.
var attributes: [NSAttributedStringKey: Any] = [:] {
didSet {
guard let existingString = super.attributedText?.string else { return }
super.attributedText = NSAttributedString(string: existingString, attributes: attributes)
}
}
/// The current text that is displayed by the label.
override var text: String? {
set { super.attributedText = NSAttributedString(string: newValue ?? "", attributes: attributes) }
get { return super.attributedText?.string }
}
/// The font used to display the text.
override var font: UIFont! {
get { return attributes[.font] as? UIFont ?? .systemFont(ofSize: 17) }
set { attributes[.font] = newValue }
}
/// The color of the text.
override var textColor: UIColor! {
get { return attributes[.foregroundColor] as? UIColor ?? .black }
set { attributes[.foregroundColor] = newValue }
}
/// The text alignment of the receiver.
override var textAlignment: NSTextAlignment {
get { return paragraphStyle?.alignment ?? .natural }
set { setParagraphValue(newValue, for: \.alignment) }
}
/// The mode that should be used to break lines in the receiver.
override var lineBreakMode: NSLineBreakMode {
get { return paragraphStyle?.lineBreakMode ?? .byWordWrapping }
set { setParagraphValue(newValue, for: \.lineBreakMode) }
}
/// The distance in points between the bottom of one line fragment and the top of the next.
var lineSpacing: CGFloat {
get { return paragraphStyle?.lineSpacing ?? 0.0 }
set { setParagraphValue(newValue, for: \.lineSpacing) }
}
/// The receiver’s minimum height.
@IBInspectable
var minimumLineHeight: CGFloat {
get { return paragraphStyle?.minimumLineHeight ?? 0 }
set { setParagraphValue(newValue, for: \.minimumLineHeight) }
}
/// The receiver’s maximum height.
@IBInspectable
var maximumLineHeight: CGFloat {
get { return paragraphStyle?.maximumLineHeight ?? 0 }
set { setParagraphValue(newValue, for: \.maximumLineHeight) }
}
/// The natural line height of the receiver is multiplied by this factor (if positive) before being constrained by minimum and maximum line height.
@IBInspectable
var lineHeightMultiple: CGFloat {
get { return paragraphStyle?.lineHeightMultiple ?? 0 }
set { setParagraphValue(newValue, for: \.lineHeightMultiple) }
}
/// This value specifies the number of points by which to adjust kern-pair characters.
@IBInspectable
var kern: CGFloat {
get { return attributes[.kern] as? CGFloat ?? 0 }
set { attributes[.kern] = newValue }
}
// MARK: Private
private var paragraphStyle: NSMutableParagraphStyle? {
return attributes[.paragraphStyle] as? NSMutableParagraphStyle
}
/// Sets a value at the specified KeyPath of ParagraphStyle.
private func setParagraphValue<T>(_ value: T, for keyPath: ReferenceWritableKeyPath<NSMutableParagraphStyle, T>) {
let style = paragraphStyle ?? NSMutableParagraphStyle()
style[keyPath: keyPath] = value
attributes[.paragraphStyle] = style
}
// MARK: Unavailable
@available(*, unavailable)
override var attributedText: NSAttributedString? { willSet { } }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment