Skip to content

Instantly share code, notes, and snippets.

@mikolasstuchlik
Last active March 24, 2023 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikolasstuchlik/100e38f3a92bb92a01004abf983f0d9c to your computer and use it in GitHub Desktop.
Save mikolasstuchlik/100e38f3a92bb92a01004abf983f0d9c to your computer and use it in GitHub Desktop.
UILabel subclass.
import UIKit
/// `Label` is an `UILabel` subclass that simplifies line height and letter spacing manipulation.
public final class Label: UILabel {
/// Since line spacing is always absolute in iOS, we need to compute the correct line spacing for given
/// font and scale.
public enum LineHeight {
case absoluteSpacing(CGFloat)
case fraction(CGFloat)
func lineSpacing(for font: UIFont) -> CGFloat {
switch self {
case .absoluteSpacing(let value):
return value
case .fraction(let value):
return value * font.pointSize - font.pointSize
}
}
}
/// Set to `true` in case you want to use dynamic scaling. Default `false`.
public var scaleFont = false {
didSet {
reformatAndStore(string: stylableText)
}
}
/// Set a non-nil value in case you want to use custom letter spacing.
public var letterSpacing: CGFloat? {
didSet {
reformatAndStore(string: stylableText)
}
}
/// Set a non-nil value in case you want to use custom line height.
public var lineHeight: LineHeight? {
didSet {
reformatAndStore(string: stylableText)
}
}
/// Font of the label, replacing the `font` property. Do not pass a scaled font.
public var unscaledFont: UIFont = .systemFont(ofSize: UIFont.systemFontSize) {
didSet {
reformatAndStore(string: stylableText)
}
}
public var additionalAttributes: [([NSAttributedString.Key : Any], Range<String.Index>)] = [] {
didSet {
reformatAndStore(string: stylableText)
}
}
/// The text if the label, replaces the `text` and `attributedText` properties.
///
/// It is not possible in the current implementation to display an attributed string.
public var stylableText: String? {
get {
super.attributedText?.string
}
set {
additionalAttributes = []
reformatAndStore(string: newValue)
}
}
@available(*, unavailable, message: "This property is not available for Label, use 'unscaledFont'.")
override public var font: UIFont! {
get { super.font }
set { super.font = newValue }
}
@available(*, unavailable, message: "This property is not available for Label, use 'stylableText'.")
override public var text: String? {
get { super.text }
set { super.text = newValue }
}
@available(*, unavailable, message: "This property is not available for Label, use 'stylableText'.")
override public var attributedText: NSAttributedString? {
get { super.attributedText }
set { super.attributedText = newValue }
}
// Observing is required so font scales correctly
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
reformatAndStore(string: stylableText)
}
@objc
private func buttonShapesEnabledStatusDidChange() {
reformatAndStore(string: stylableText)
}
private func reformatAndStore(string: String?) {
super.attributedText = string.flatMap { text in
var attrStr = NSMutableAttributedString(string: text, attributes: attributes)
additionalAttributes.forEach { (custom, range) in
attrStr.addAttributes(custom, range: NSRange(range, in: text))
}
return attrStr
}
}
private var attributes: [NSAttributedString.Key: Any] {
var attributes = [NSAttributedString.Key: Any]()
let designatedFont = scaleFont ? unscaledFont : UIFontMetrics.default.scaledFont(for: unscaledFont)
super.font = designatedFont
attributes[.font] = designatedFont
if let letterSpacing = letterSpacing {
attributes[.kern] = letterSpacing
}
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = textAlignment
paragraphStyle.lineBreakMode = .byTruncatingTail
if let lineHeight = lineHeight {
paragraphStyle.lineSpacing = lineHeight.lineSpacing(for: designatedFont)
}
attributes[.paragraphStyle] = paragraphStyle
return attributes
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment