diff --git a/VDS/Components/Label/Attributes/LabelAttributeModel.swift b/VDS/Components/Label/Attributes/LabelAttributeModel.swift index c3209f68..8d5c02e1 100644 --- a/VDS/Components/Label/Attributes/LabelAttributeModel.swift +++ b/VDS/Components/Label/Attributes/LabelAttributeModel.swift @@ -22,5 +22,32 @@ extension LabelAttributeModel { public static func == (lhs: any LabelAttributeModel, rhs: any LabelAttributeModel) -> Bool { lhs.isEqual(rhs) } +} + +public extension NSAttributedString { + 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)? { + switch key { + case NSAttributedString.Key.font: + let value = value as! UIFont + let style = TypographicalStyle.style(for: value.fontName, size: value.pointSize) + return FontLabelAttribute(location: range.location, length: range.length, style: style) + case NSAttributedString.Key.foregroundColor: + return ColorLabelAttribute(location: range.location, length: range.length, color: value as! UIColor) + default: + return nil + } + } } diff --git a/VDS/Typography/Typography.swift b/VDS/Typography/Typography.swift index e8b8c2ba..92ff5e9a 100644 --- a/VDS/Typography/Typography.swift +++ b/VDS/Typography/Typography.swift @@ -261,3 +261,20 @@ extension TypographicalStyle { } } +extension TypographicalStyle { + public static func style(for fontName: String, size: CGFloat) -> TypographicalStyle { + //filter all styles by fontName + let styles = allCases.filter{$0.fontFace.fontName == fontName }.sorted { lhs, rhs in lhs.pointSize < rhs.pointSize } + //if there are no styles then return defaultStyle + guard styles.count > 0 else { return defaultStyle } + styles.forEach{print("\($0.rawValue): \($0.pointSize)")} + //if there is an exact match on a style with this pointSize then return it + if let style = styles.first(where: {$0.pointSize == size }) { + return style + } else if let largerIndex = styles.firstIndex(where: { $0.pointSize > size}) { //find the closet one to pointSize + return styles[max(largerIndex - 1, 0)] + } else { //return the last style + return styles.last! + } + } +}