// // LabelAttributeModel.swift // VDS // // Created by Matt Bruce on 8/3/22. // import Foundation import UIKit public protocol LabelAttributeModel: AnyEquatable, Withable, Equatable, Identifiable where ID == UUID { /// Position of the starting point for this LabelAttribute. var location: Int { get set } /// Length from the location. var length: Int { get set } /// Applies the attribute to the attributedString that is given. /// - Parameter attributedString: AttributedString that the attributed is applied. func setAttribute(on attributedString: NSMutableAttributedString) } extension LabelAttributeModel { /// Range for this AttributeModel public var range: NSRange { NSRange(location: location, length: length) } public static func == (lhs: any LabelAttributeModel, rhs: any LabelAttributeModel) -> Bool { lhs.isEqual(rhs) } public func isValidRange(on attributedString: NSMutableAttributedString) -> Bool { attributedString.isValid(range: range) } } public extension String { func isValid(range: NSRange) -> Bool { range.location >= 0 && range.length > 0 && range.location + range.length <= count } } public extension NSAttributedString { func isValid(range: NSRange) -> Bool { range.location >= 0 && range.length > 0 && range.location + range.length <= length } func createAttributeModels() -> [(any LabelAttributeModel)] { var attributes: [any VDS.LabelAttributeModel] = [] enumerateAttributes(in: NSMakeRange(0, length)) { attributeMap, range, stop in attributeMap.forEach { (key: NSAttributedString.Key, value: Any) in if let attribute = NSAttributedString.createAttributeModelFor(key: key, range: range, value: value) { attributes.append(attribute) } } } return attributes } static func createAttributeModelFor(key: NSAttributedString.Key, range: NSRange, value: Any) -> (any LabelAttributeModel)? { guard let value = value as? AnyHashable else { return nil } guard let font = value as? UIFont, let style = TextStyle.style(for: font.fontName, size: font.pointSize), key == .font else { return AnyAttribute(location: range.location, length: range.length, key: key, value: value) } return TextStyleLabelAttribute(location: range.location, length: range.length, textStyle: style) } } extension NSMutableAttributedString { public func apply(attribute: any LabelAttributeModel) { guard isValid(range: attribute.range) else { return } attribute.setAttribute(on: self) } public func apply(attributes: [any LabelAttributeModel]) { attributes.forEach { apply(attribute: $0) } } }