Last active
January 20, 2020 04:39
-
-
Save lionhylra/d70d98de6af27d801cba4e34a9838383 to your computer and use it in GitHub Desktop.
A NSAttributedString extension that adds a convenient method for composing a NSAttributedString with links and images.
This file contains 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 UIKit | |
// How to use: | |
/* | |
let label = UILabel() | |
let textView = UITextView() | |
label.attributedText = .compose( | |
"Please go to ", | |
.link(label: "Google", | |
url: URL(string: "https://google.com")!), | |
" ", .systemImage("globe") | |
) | |
let style = NSAttributedStringStyle(font: .preferredFont(forTextStyle: .body), lineSpacing: 16) | |
textView.attributedText = .compose(style: style, | |
"Please go to ", .linkLiteral("https://google.com"), " for more details.", .systemImage("globe"), | |
.lineBreak, | |
"Also you can go to our ", .link(label: "Website", url: URL(string: "http://google.com")!), ".", | |
.lineBreak, | |
"This is another line.", .systemImage("arrow.up.bin") | |
) | |
*/ | |
extension NSAttributedString { | |
static func compose(style: NSAttributedStringStyle = .default, _ components: NSAttributedStringComponent...) -> NSAttributedString { | |
let mutableAttributedString = NSMutableAttributedString() | |
for c in components { | |
mutableAttributedString.append(c.attributedString()) | |
} | |
let range = NSRange(location: 0, length: mutableAttributedString.length) | |
mutableAttributedString.addAttributes(style.attributes(), range: range) | |
return mutableAttributedString | |
} | |
} | |
struct NSAttributedStringStyle { | |
static let `default` = NSAttributedStringStyle() | |
let font: UIFont? | |
let foregroundColor: UIColor? | |
let alignment: NSTextAlignment? | |
let lineSpacing: CGFloat? | |
let paragraphSpacing: CGFloat? | |
init(font: UIFont? = nil, foregroundColor: UIColor? = nil, alignment: NSTextAlignment? = nil, lineSpacing: CGFloat? = nil, paragraphSpacing: CGFloat? = nil) { | |
self.font = font | |
self.foregroundColor = foregroundColor | |
self.alignment = alignment | |
self.lineSpacing = lineSpacing | |
self.paragraphSpacing = paragraphSpacing | |
} | |
func attributes() -> [NSAttributedString.Key: Any] { | |
var attributes: [NSAttributedString.Key: Any] = [:] | |
attributes[.font] = font | |
attributes[.foregroundColor] = foregroundColor | |
let paragraphStyle = NSMutableParagraphStyle() | |
if let alignment = alignment { | |
paragraphStyle.alignment = alignment | |
} | |
if let lineSpacing = lineSpacing { | |
paragraphStyle.lineSpacing = lineSpacing | |
} | |
if let paragraphSpacing = paragraphSpacing { | |
paragraphStyle.paragraphSpacing = paragraphSpacing | |
} | |
attributes[.paragraphStyle] = paragraphStyle | |
return attributes | |
} | |
} | |
enum NSAttributedStringComponent { | |
case text(String) | |
case link(label: String, url: URL) | |
case linkLiteral(String) | |
case image(UIImage?, CGSize? = nil) | |
@available(iOS 13.0, *) | |
case systemImage(String, tintColor: UIColor? = nil) | |
case lineBreak | |
case space | |
func attributedString() -> NSAttributedString { | |
switch self { | |
case .text(let string): | |
return NSAttributedString(string: string) | |
case .link(let label, let url): | |
return NSAttributedString(string: label, attributes: [.link: url]) | |
case .linkLiteral(let string): | |
assert(URL(string: string) != nil) | |
return Self.link(label: string, url: URL(string: string)!).attributedString() | |
case .image(let image, let size): | |
let attachment = NSTextAttachment() | |
attachment.image = image | |
if let size = size { | |
attachment.bounds.size = size | |
} | |
return NSAttributedString(attachment: attachment) | |
case .systemImage(let name, let tintColor): | |
if #available(iOS 13.0, *) { | |
var image = UIImage(systemName: name) | |
if let tintColor = tintColor { | |
image = image?.withTintColor(tintColor) | |
} | |
return Self.image(image).attributedString() | |
} else { | |
let image = UIImage(named: name) | |
return Self.image(image).attributedString() | |
} | |
case .lineBreak: | |
return NSAttributedString(string: "\n") | |
case .space: | |
return Self.text(" ").attributedString() | |
} | |
} | |
} | |
extension NSAttributedStringComponent: ExpressibleByStringLiteral { | |
init(stringLiteral value: String) { | |
self = .text(value) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment