Skip to content

Instantly share code, notes, and snippets.

@JadenGeller
Last active March 26, 2022 03:24
Show Gist options
  • Save JadenGeller/101909ddc62729d43f7a to your computer and use it in GitHub Desktop.
Save JadenGeller/101909ddc62729d43f7a to your computer and use it in GitHub Desktop.
Interpolated Attributed String Builder
// Example
var brown: AttributedStringSegment = "brown"
brown.foregroundColor = UIColor.brownColor()
brown.expansion = 1.1
var lazy: AttributedStringSegment = "lazy"
lazy.strikethroughStyle = NSUnderlineStyle.StyleDouble
lazy.strikethroughColor = UIColor.redColor()
let fox = UIImage(named: "fox.jpg")!
let text = attributedString("The quick \(brown) \(fox) jumped over the \(lazy) dog.")
// AND WE GET SOMETHING LIKE THIS!
// https://twitter.com/JadenGeller/status/614286532875194368
func attributedString(builder: AttributedStringBuilder) -> NSAttributedString {
return builder.attributedString
}
struct AttributedStringBuilder : StringInterpolationConvertible, StringLiteralConvertible {
private let attributedString: NSAttributedString
init(stringInterpolation strings: AttributedStringBuilder...) {
let builder = NSMutableAttributedString()
for string in strings {
builder.appendAttributedString(string.attributedString)
}
self.attributedString = NSAttributedString(attributedString: builder)
}
init<T>(stringInterpolationSegment expr: T) {
attributedString = "\(expr)".attributedStringRepresentation
}
init<T : AttributedStringConvertible>(stringInterpolationSegment expr: T) {
attributedString = expr.attributedStringRepresentation
}
init(string: String) {
attributedString = NSAttributedString(string: string)
}
init(stringLiteral value: String) {
attributedString = NSAttributedString(string: value)
}
init(extendedGraphemeClusterLiteral value: String) {
attributedString = NSAttributedString(string: value)
}
init(unicodeScalarLiteral value: String) {
attributedString = NSAttributedString(string: value)
}
}
protocol AttributedStringConvertible {
var attributedStringRepresentation: NSAttributedString { get }
}
extension NSAttributedString {
var attributedStringRepresentation: NSAttributedString {
get {
return self
}
}
}
struct AttributedStringSegment : AttributedStringConvertible, StringLiteralConvertible {
let text: String
init(_ text: String) {
self.text = text
}
init(stringLiteral value: String) {
text = value
}
init(extendedGraphemeClusterLiteral value: String) {
text = value
}
init(unicodeScalarLiteral value: String) {
text = value
}
var font: UIFont!
var paragraphStyle: NSParagraphStyle!
var foregroundColor: UIColor!
var backgroundColor: UIColor!
var kern: Float!
var strikethroughStyle: NSUnderlineStyle!
var underlineStyle: NSUnderlineStyle!
var strokeColor: UIColor!
var strokeWidth: Float!
var shadow: NSShadow!
var link: NSURL!
var baselineOffset: Float!
var underlineColor: UIColor!
var strikethroughColor: UIColor!
var obliqueness: Float!
var expansion: Float!
var writingDirection: [NSWritingDirection]!
var vertical: Bool!
var attributedStringRepresentation: NSAttributedString {
get {
var attributes = [String : NSObject]()
if let font = font {
attributes[NSFontAttributeName] = font
}
if let paragraphStyle = paragraphStyle {
attributes[NSParagraphStyleAttributeName] = paragraphStyle
}
if let foregroundColor = foregroundColor {
attributes[NSForegroundColorAttributeName] = foregroundColor
}
if let backgroundColor = backgroundColor {
attributes[NSBackgroundColorAttributeName] = backgroundColor
}
if let kern = kern {
attributes[NSKernAttributeName] = kern
}
if let strikethroughStyle = strikethroughStyle {
attributes[NSStrikethroughStyleAttributeName] = strikethroughStyle.rawValue
}
if let underlineStyle = underlineStyle {
attributes[NSUnderlineStyleAttributeName] = underlineStyle.rawValue
}
if let strokeColor = strokeColor {
attributes[NSStrokeColorAttributeName] = strokeColor
}
if let strokeWidth = strokeWidth {
attributes[NSStrokeWidthAttributeName] = strokeWidth
}
if let shadow = shadow {
attributes[NSShadowAttributeName] = shadow
}
if let link = link {
attributes[NSLinkAttributeName] = link
}
if let baselineOffset = baselineOffset {
attributes[NSBaselineOffsetAttributeName] = baselineOffset
}
if let underlineColor = underlineColor {
attributes[NSUnderlineColorAttributeName] = underlineColor
}
if let strikethroughColor = strikethroughColor {
attributes[NSStrikethroughColorAttributeName] = strikethroughColor
}
if let obliqueness = obliqueness {
attributes[NSObliquenessAttributeName] = obliqueness
}
if let expansion = expansion {
attributes[NSExpansionAttributeName] = expansion
}
if let writingDirection = writingDirection {
attributes[NSWritingDirectionAttributeName] = writingDirection.map { $0.rawValue }
}
if let vertical = vertical {
attributes[NSVerticalGlyphFormAttributeName] = vertical ? 1 : 0
}
return NSAttributedString(string: text, attributes: attributes)
}
}
}
extension NSURL : AttributedStringConvertible {
var attributedStringRepresentation: NSAttributedString {
get {
var segment = AttributedStringSegment(absoluteString)
segment.link = self
return segment.attributedStringRepresentation
}
}
}
extension String : AttributedStringConvertible {
var attributedStringRepresentation: NSAttributedString {
get {
return NSAttributedString(string: self)
}
}
}
extension NSTextAttachment {
var attributedStringRepresentation: NSAttributedString {
get {
return NSAttributedString(attachment: self)
}
}
}
extension UIImage : AttributedStringConvertible {
var attributedStringRepresentation: NSAttributedString {
get {
let attachment = NSTextAttachment()
attachment.image = self
return attachment.attributedStringRepresentation
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment