Last active
May 13, 2021 20:49
-
-
Save JasonCanCode/c890b280abb20ab5c15a3e498c5daba6 to your computer and use it in GitHub Desktop.
Extending NSAttributedString with a convenient way to generate text with mixed attributes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
extension NSAttributedString { | |
struct FontTypes: OptionSet { | |
static let `default` = FontTypes(rawValue: 1 << 0) | |
static let bold = FontTypes(rawValue: 1 << 1) | |
static let italic = FontTypes(rawValue: 1 << 2) | |
static let underlined = FontTypes(rawValue: 1 << 3) | |
let rawValue: Int8 | |
} | |
/** | |
Generates an Attributed String by iterating through an array of strings with associated FontTypes. | |
Example use: | |
thoughtBubbleLabel.attributedText = NSAttributedString.fromComponents([ | |
( "\"What a handy extension,\" ", .italic ), | |
( "he thought to himself. ", .default ), | |
( "\n", .default ), | |
( "Indeed. ", .bold ), | |
( "It was.", [.underlined, .bold] ), | |
]) | |
Things to consider: | |
- When providing FontTypes, `[]` and `.default` produce the same results. | |
- When supplying a font, you can technically provide a bold font. Doing so would mean `[]`, `.default`, and `.bold` produce the same results. | |
- parameter components: A tuple array of strings with associated FontTypes. | |
- parameter font: Provide a font if you don't want to use the default font. | |
*/ | |
static func fromComponents(_ components: [(String, FontTypes)], font: UIFont? = nil) -> NSAttributedString { | |
let font = font ?? .size7 | |
let attrString = NSMutableAttributedString() | |
for component in components { | |
let (text, types) = component | |
var attr: [NSAttributedString.Key: Any] = [:] | |
var traits: UIFontDescriptor.SymbolicTraits = [] | |
if font.fontDescriptor.symbolicTraits.contains(.traitBold) | |
|| types.contains(.bold) { | |
traits.insert(.traitBold) | |
} | |
if font.fontDescriptor.symbolicTraits.contains(.traitItalic) | |
|| types.contains(.italic) { | |
traits.insert(.traitItalic) | |
} | |
if let desc = font.fontDescriptor.withSymbolicTraits(traits) { | |
attr[.font] = UIFont(descriptor: desc, size: font.pointSize) | |
} else { | |
attr[.font] = font | |
} | |
if types.contains(.underlined) { | |
attr[.underlineStyle] = NSUnderlineStyle.single.rawValue | |
} | |
attrString.append(NSAttributedString(string: text, attributes: attr)) | |
} | |
return attrString | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension XCTestCase { | |
/// Verify that a range of text within an attributed string has the correct attributes | |
/// - Parameters: | |
/// - attrText: Full attributed string to be inspected at provided range | |
/// - expectedRange: Range within `attrText` to verify | |
/// - link: Path string of an embedded link if one should be present | |
/// - fontTypes: What font types should be present if at all | |
func verifySubAttributes( | |
attrText: NSAttributedString, | |
expectedRange: NSRange, | |
link: String? = nil, | |
fontTypes: NSAttributedString.FontTypes = [] | |
) { | |
var range = NSRange(location: 0, length: attrText.length) | |
let attributes = attrText.attributes(at: expectedRange.location, effectiveRange: &range) | |
XCTAssertEqual(range, expectedRange) | |
XCTAssertEqual(attributes[.link] as? String, link) | |
if fontTypes.contains(.underlined) { | |
XCTAssertEqual(attributes[.underlineStyle] as? Int, NSUnderlineStyle.single.rawValue) | |
} | |
guard let font = attributes[.font] as? UIFont else { | |
return | |
} | |
if fontTypes.contains(.bold) { | |
XCTAssertTrue(font.fontDescriptor.symbolicTraits.contains(.traitBold)) | |
} | |
if fontTypes.contains(.italic) { | |
XCTAssertTrue(font.fontDescriptor.symbolicTraits.contains(.traitItalic)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment