AttributedString - 基于Swift插值方式优雅地构建富文本
🇨🇳 天朝子民
功能
- 使用插值构造丰富文本,平滑编码,优雅自然风格。
- 更多控制扩展支持。
- 支持多级丰富文本层叠,并提供了其他样式优先级策略。
- 支持所有
NSAttributedString.Key
函数。 - 支持 iOS & macOS & watchOS & tvOS。
- 支持文本和附件点击或按压事件回调,支持高亮样式。
- 支持异步图像附件,你可以将远程图像加载到
UITextView
中。 - 支持视图附件,你可以将自定义视图添加到
UITextView
。 - 继续添加更多新功能。
截图
安装
CocoaPods - Podfile
pod 'AttributedString'
Carthage - Cartfile
github "lixiang1994/AttributedString"
Swift Package Manager for Apple platforms
选择Xcode菜单 文件 > Swift包 > 添加包依赖
并使用图形用户界面输入仓库URL。
Repository: https://github.com/lixiang1994/AttributedString
Swift包管理器
在您的Package.swift
的依赖项中添加以下内容
.package(url: "https://github.com/lixiang1994/AttributedString.git", from: "version")
使用方法
首先请确保导入框架
import AttributedString
如何初始化
// Normal
let a: ASAttributedString = .init("lee", .font(.systemFont(ofSize: 13)))
// Interpolation
let b: ASAttributedString = "\("lee", .font(.systemFont(ofSize: 13)))"
以下是一些使用示例。所有设备也作为模拟器提供
字体
textView.attributed.text = """
\("fontSize: 13", .font(.systemFont(ofSize: 13)))
\("fontSize: 20", .font(.systemFont(ofSize: 20)))
\("fontSize: 22 weight: semibold", .font(.systemFont(ofSize: 22, weight: .semibold)))
"""
前景色
textView.attributed.text = """
\("foregroundColor", .foreground(.white))
\("foregroundColor", .foreground(.red))
"""
删除线
textView.attributed.text = """
\("strikethrough: single", .strikethrough(.single))
\("strikethrough: double color: .red", .strikethrough(.double, color: .red))
"""
附件:(不包括watchOS)
// ASAttributedString.Attachment
textView.attributed.text = """
\(.data(xxxx, type: "zip"))
\(.file(try!.init(url: .init(fileURLWithPath: "xxxxx"), options: [])))
\(.attachment(NSTextAttachment()))
"""
附件图片:(不包括watchOS)
// ASAttributedString.ImageAttachment
textView.attributed.text = """
\(.image(UIImage(named: "xxxx")))
\(.image(UIImage(named: "xxxx"), .custom(size: .init(width: 200, height: 200))))
\(.image(UIImage(named: "xxxx"), .proposed(.center))).
"""
附件异步图片:(仅支持iOS:UITextView)
// ASAttributedString.AsyncImageAttachment
textView.attributed.text = """
\(.image(url, placeholder: xxxxx))
"""
自定义加载
ASAttributedString.AsyncImageAttachment.Loader = AsyncImageAttachmentKingfisherLoader.self
请阅读AttachmentViewController.swift
示例文件以获取详细信息。
附件视图:(仅支持iOS:UITextView)
// ASAttributedString.ViewAttachment
textView.attributed.text = """
\(.view(xxxxView))
\(.view(xxxxView, .custom(size: .init(width: 200, height: 200))))
\(.view(xxxxView, .proposed(.center))).
"""
Wrap
let a: ASAttributedString = .init("123", .background(.blue))
let b: ASAttributedString = .init("456", .background(.red))
textView.attributed.text = "\(wrap: a) \(wrap: b, .paragraph(.alignment(.center)))"
// Defalut embedding mode, Nested internal styles take precedence over external styles
textView.attributed.text = "\(wrap: a, .paragraph(.alignment(.center)))"
textView.attributed.text = "\(wrap: .embedding(a), .paragraph(.alignment(.center)))"
// Override mode, Nested outer style takes precedence over inner style
textView.attributed.text = "\(wrap: .override(a), .paragraph(.alignment(.center)))"
Append
let a: ASAttributedString = .init("123", .background(.blue))
let b: ASAttributedString = .init("456", .background(.red))
let c: ASAttributedString = .init("789", .background(.gray))
textView.attributed.text = a + b
textView.attributed.text += c
Checking
var string: ASAttributedString = .init("my phone number is +86 18611401994.", .background(.blue))
string.add(attributes: [.foreground(color)], checkings: [.phoneNumber])
textView.attributed.text = string
var string: ASAttributedString = .init("open https://www.apple.com and https://github.com/lixiang1994/AttributedString", .background(.blue))
string.add(attributes: [.foreground(color)], checkings: [.link])
textView.attributed.text = string
var string: ASAttributedString = .init("123456789", .background(.blue))
string.add(attributes: [.foreground(color)], checkings: [.regex("[0-6]")])
textView.attributed.text = string
操作:(仅支持iOS:UILabel / UITextView & macOS:NSTextField)
对于复杂的样式,建议使用UITextView。
需要将UITextView的isEditable
和isSelectable
设置为false
。
点击
// Text
let a: ASAttributedString = .init("lee", .action({ }))
// Attachment (image)
let b: ASAttributedString = .init(.image(image), action: {
// code
})
// It is recommended to use functions as parameters.
func clicked() {
// code
}
// Normal
let c: ASAttributedString = .init("lee", .action(clicked))
let d: ASAttributedString = .init(.image(image), action: clicked)
// Interpolation
let e: ASAttributedString = "\("lee", .action(clicked))"
let f: ASAttributedString = "\(.image(image), action: clicked)"
// More information.
func clicked(_ result: ASAttributedString.Action.Result) {
switch result.content {
case .string(let value):
print("Currently clicked text: \(value) range: \(result.range)")
case .attachment(let value):
print("Currently clicked attachment: \(value) range: \(result.range)")
}
}
label.attributed.text = "This is \("Label", .font(.systemFont(ofSize: 20)), .action(clicked))"
textView.attributed.text = "This is a picture \(.image(image, .custom(size: .init(width: 100, height: 100))), action: clicked) Displayed in custom size."
按下
func pressed(_ result: ASAttributedString.Action.Result) {
switch result.content {
case .string(let value):
print("Currently pressed text: \(value) range: \(result.range)")
case .attachment(let value):
print("Currently pressed attachment: \(value) range: \(result.range)")
}
}
label.attributed.text = "This is \("Long Press", .font(.systemFont(ofSize: 20)), .action(.press, pressed))"
textView.attributed.text = "This is a picture \(.image(image, .custom(size: .init(width: 100, height: 100))), trigger: .press, action: pressed) Displayed in custom size."
高亮样式
func clicked(_ result: ASAttributedString.Action.Result) {
switch result.content {
case .string(let value):
print("Currently clicked text: \(value) range: \(result.range)")
case .attachment(let value):
print("Currently clicked attachment: \(value) range: \(result.range)")
}
}
label.attributed.text = "This is \("Label", .font(.systemFont(ofSize: 20)), .action([.foreground(.blue)], clicked))"
自定义
let custom = ASAttributedString.Action(.press, highlights: [.background(.blue), .foreground(.white)]) { (result) in
switch result.content {
case .string(let value):
print("Currently pressed text: \(value) range: \(result.range)")
case .attachment(let value):
print("Currently pressed attachment: \(value) range: \(result.range)")
}
}
label.attributed.text = "This is \("Custom", .font(.systemFont(ofSize: 20)), .action(custom))"
textView.attributed.text = "This is a picture \(.image(image, .original(.center)), action: custom) Displayed in original size."
注意:(仅支持iOS:UILabel / UITextView & macOS:NSTextField)
label.attributed.observe([.phoneNumber], highlights: [.foreground(.blue)]) { (result) in
print("Currently clicked \(result)")
}
textView.attributed.observe([.link], highlights: [.foreground(.blue)]) { (result) in
print("Currently clicked \(result)")
}
关于更多示例,请查看示例应用。
Attribute
类可用的属性
通过以下属性可用
属性 | 类型 | 描述 |
---|---|---|
font | UIFont |
font |
color | UIColor |
foreground color |
background | UIColor |
background color |
paragraph | ParagraphStyle |
paragraph attributes |
ligature | Bool |
Ligatures cause specific character combinations to be rendered using a single custom glyph that corresponds to those characters |
kern | CGFloat |
kerning |
strikethrough | NSUnderlineStyle . UIColor |
strikethrough style and color (if color is nil foreground is used) |
underline | NSUnderlineStyle , UIColor |
下划线样式和颜色(如果颜色为空,则使用前景色) |
链接 | String / URL |
URL |
基线偏移量 | CGFloat |
字符相对于基线的偏移量,以点为单位 |
阴影 | NSShadow |
文本的阴影效果 |
描边 | CGFloat , UIColor |
描边宽度和颜色 |
文本效果 | NSAttributedString.TextEffectStyle |
文本效果 |
倾斜度 | CGFloat |
文本倾斜度 |
扩张 | CGFloat |
扩张/收缩 |
书写方向 | WritingDirection / [Int] |
用于确定文本实际书写方向的初始书写方向 |
垂直字体形式 | Bool |
垂直字形(在iOS上,它始终是水平的。) |
Attribute.Checking
枚举可用的案例
通过 CASE | 描述 |
---|---|
范围(NSRange) |
自定义范围 |
正则表达式(String) |
正则表达式 |
操作 |
操作 |
日期 |
日期(基于 NSDataDetector ) |
链接 |
链接(基于 NSDataDetector ) |
地址 |
地址(基于 NSDataDetector ) |
电话号码 |
电话号码(基于 NSDataDetector ) |
交通信息 |
交通信息(基于 NSDataDetector ) |
贡献
如果您需要实现特定功能或遇到了bug,请打开一个issue。如果您扩展了AttributedString的功能并希望其他人也能使用,请提交一个pull request。
许可
AttributedString在MIT许可下。有关更多信息,请参阅LICENSE文件。
欢迎加入群交流