StringEx
StringEx
使创建 NSAttributedString
和操作 String
变得容易。
目录
快速示例
这个简单的示例让你了解库的实际功能。
let string = "<title /><address class=\"line\">Address: Cupertino, CA 95014</address><phone class=\"line\">Phone: (408) 996–1010</phone><site class=\"line\">Site: https://apple.com</site>"
// Create StringEx instance
let ex = string.ex
// Apply default styles to whole string
ex.style([
.font(.systemFont(ofSize: 17.0)),
.color(.black)
])
// Insert company name and style it
ex[.tag("title")]
.insert("Apple")
.style(.font(.boldSystemFont(ofSize: 24.0)))
// Add new lines to each tag with `line` class
ex[.class("line")].prepend("\n")
// Add some space before each paragraph and set text aligment
ex[.all].style([
.paragraphSpacingBefore(10.0),
.aligment(.center)
])
// Get site url
let selector = .tag("site") => .regex("(?i)https?://(?:www\\.)?\\S+(?:/|\\b)")
let url = ex[selector].selectedString
// Attach url to site link and style it
ex[selector].style([
.linkString(url),
.color(.blue),
.underlineStyle(.single, color: .blue)
])
// Apply gray color to contacts captions
ex[.string("address:") + .string("phone:") + .string("site:")].style(.color(.gray))
// Get result attributed string
let attributedString = ex.attributedString
// and display it in TextView
textView.attributedText = attributedString
textView.dataDetectorTypes = .link
结果,我们得到如下内容
安装
- 要求
- iOS 10.0+
- Swift 5.2 (Xcode 11.4+)
- 依赖
- 无
CocoaPods
您可以使用 CocoaPods 通过将其添加到您的 Podfile
中来安装 StringEx
platform :ios, '10.0'
use_frameworks!
target 'MyApp' do
pod 'StringEx'
end
初始化
首先在 swift 文件中包含库
import StringEx
创建 StringEx
实例
// Creating with an initializer from a string
var ex = StringEx(string: "Hello, World!")
// Creating with an initializer from a NSAttributedString
ex = StringEx(attributedString: NSAttributedString(string: "Hello, World!"))
// Shorthand method for string
ex = "Hello, World!".ex
// Shorthand method for NSAttributedString
ex = NSAttributedString(string: "Hello, World!").ex
字符串选择器
字符串选择器是
执行选择器有两种方式
let ex = "Hello, World!".ex
// Using select method
ex.select(.string("world")).style(.color(.red))
// or using subscript on StringEx instance
ex[.string("world")].style(.color(.red))
这些方法将内部指针设置为传入的选择器并返回相同的对象。这使得您可以在一个语句中链式(对同一实例)执行其他方法。
执行选择器后的结果是一个按其下限排序的范围数组。如果存在重叠的范围,它们将合并为一个范围。
以下类型的选择器可用
HTML 标签
StringEx
可以处理 HTML 字符串,允许您通过标签名、类或标识符选择 HTML 标签内的子字符串。HTML 标签语法必须符合以下规范:https://html.whatwg.com.cn/multipage/syntax.html
let ex = "Example: <p id="example"><span class="word1">Hello</span>, <span class="word2">World</span>!</p>".ex
// Select by tag name
let str1 = ex[.tag("span")].selectedString
print(str1) // HelloWorld
// Select by tag class
let str2 = ex[.class("word1")].selectedString
print(str2) // Hello
// Select by tag identifier
let str3 = ex[.id("example")].selectedString
print(str3) // Hello, World!
您还可以使用自闭合标签
let ex = "Hello, <name />!".ex
let str = ex[.tag("name")].insert("World").string
print(str) // Hello, World!
子字符串
let ex = "Hello, World!".ex
// Case insensitive search
let str1 = ex[.string("hello")].selectedString
print(str1) // Hello
// Case sensitive search
let str2 = ex[.string("World", caseInsensitive: false)].selectedString
print(str2) // World
let str3 = ex[.string("world", caseInsensitive: false)].selectedString
print(str3.isEmpty) // true
正则表达式
let ex = "Hello, World!".ex
// Select only latin symbols
let str1 = ex[.regex("[a-zA-Z]")].selectedString
print(str1) // HelloWorld
// Using NSRegularExpression.Options
let str2 = ex[.regex("[a-z]", options: [.caseInsensitive])].selectedString
print(str2) // HelloWorld
范围
StringEx
使用 Range<Int>
来处理范围。索引对应字符串中显示的每个字符,其中第一个字符位于索引 0
,最后一个字符位于索引 string.count - 1
在 HTML 字符串中,索引对应于不带标签的字符串中的字符
let ex = "Hello, World!".ex
// Select first 5 symbols
let str1 = ex[.range(0..<5)].selectedString
print(str1) // Hello
// It is safe to pass a range that is out of range
let str2 = ex[.range(-Int.max..<Int.max)].selectedString
print(str2) // Hello, World!
重置
除了上述选择器之外,还有一个 .all
选择器,允许您选择整个字符串。
let ex = "<span>Hello</span>, World!".ex
let str1 = ex[.tag("span")].selectedString
print(str1) // Hello
// Reset selector
let str2 = ex[.all].selectedString
print(str2) // Hello, World!
嵌套选择器
您可以使用嵌套选择器在父选择器内部进行搜索。
// Select first character of each span tag
let selector1: StringSelector = .tag("span").select(.range(0..<1))
// or do the same using the shorthand operator =>
let selector2: StringSelector = .tag("span") => .range(0..<1)
print(selector1 == selector2) // true
let ex = "<b><span>Hello</span></b>, <span><b>World</b></span>!".ex
let str = ex[.tag("span") => .tag("b") => .range(0..<1)].selectedString
print(str) // W
选择器联合
您可以将多个选择器组合成一个单个语句以执行搜索。
// Select inner contents of each span and em tags
let selector1: StringSelector = .tag("span").add(.tag("em"))
// or do the same using the shorthand operator +
let selector2: StringSelector = .tag("span") + .tag("em")
print(selector1 == selector2) // true
let ex = "<span>Hello</span>, <em>World</em>!".ex
let str = ex[.tag("span") + .tag("em")].selectedString
print(str) // HelloWorld
筛选选择器
您可以使用筛选选择器来筛选选择器的结果,以获取所需结果的子集。
// Get the first result of the selector
let selector1: StringSelector = .tag("span").filter(.first)
// or do the same using the shorthand operator %
let selector2: StringSelector = .tag("span") % .first
print(selector1 == selector2) // true
let ex = "<b>H</b><b>e</b><b>l</b><b>l</b><b>o</b>, <b>W</b><b>o</b><b>r</b><b>l</b><b>d</b>!".ex
// Reduces the set of the selector results to the first in the set
let str1 = ex[.tag("b") % .first].selectedString
print(str1) // H
// Reduces the set of the selector results to the last in the set
let str2 = ex[.tag("b") % .last].selectedString
print(str2) // d
// Reduces the set of the selector results to the one at the specified index
let str3 = ex[.tag("b") % .eq(5)].selectedString
print(str3) // W
// Reduces the set of the selector results to even ones in the set
let str4 = ex[.tag("b") % .even].selectedString
print(str4) // Hlool
// Reduces the set of the selector results to odd ones in the set
let str5 = ex[.tag("b") % .odd].selectedString
print(str5) // elWrd
// Select all selector results at the index greater than index within the set
let str6 = ex[.tag("b") % .gt(4)].selectedString
print(str6) // World
// Select all selector results at the index less than index within the set
let str7 = ex[.tag("b") % .lt(5)].selectedString
print(str7) // Hello
优先级
您可以使用括号改变操作符的顺序,与常规算术表达式类似。操作符=>
和%
具有相同的优先级,高于操作符,
的优先级。
let ex = "<span><b>Hello</b></span>, <em><b>World</b></em>!".ex
let selector1: StringSelector = .tag("span") + .tag("em") => .range(0..<1)
print(ex[selector1].selectedString) // HelloW
let selector2: StringSelector = (.tag("span") + .tag("em")) => .range(0..<1)
print(ex[selector2].selectedString) // HW
let selector3: StringSelector = .tag("span") => .tag("b") % .last
print(ex[selector3].selectedString) // Hello
let selector4: StringSelector = .tag("span") => (.tag("b") % .last)
print(ex[selector4].selectedString.isEmpty) // true
获取器
StringEx
具有几个有用的属性和方法,可以获取您所需的所有信息。
选择器
let ex = "<span>Hello</span>, <span>World</span>!".ex
// Get the current selector
print(ex.selector) // all
ex.select(.tag("span"))
// Get the current selector
print(ex.selector) // tag("span")
// Get the number of found sub-ranges
print(ex.count) // 2
字符串
要获取原始字符串(包含HTML标签),您可以使用rawString
属性。在创建包含HTML标签的StringEx
实例时,库会尝试修复可能的标记错误,例如缺少结束标签等。在这种情况下,rawString
属性将包含修正后的HTML字符串。
let ex = "Hello, <b>World!".ex
print(ex.rawString) // Hello, <b>World!</b>
要获取不含HTML标签的整个String
,您可以使用string
属性。该属性始终包含整个字符串,无论当前应用到StringEx
实例的选择器如何。
let ex = "Hello, <b>World</b>!".ex
print(ex.string) // Hello, World!
ex.select(.tag("b"))
print(ex.string) // Hello, World!
您可以使用selectedString
属性或selectedString(separator: String)
方法获取字符串的选定子范围。
由于选择器的结果是子范围数组,因此要获取选定的字符串,必须传递一个用作组合选定子字符串的分隔符。在使用
selectedString
属性的情况下,分隔符是默认的空字符串""
。
let ex = "<span>Hello</span>, <span>World</span>!".ex
ex.select(.tag("span"))
// Using the property
print(ex.selectedString) // HelloWorld
// Using the method
print(ex.selectedString(separator: "-")) // Hello-World
// Overlapping ranges are combined into one
ex.select(.tag("span") + .range(0..<2) + .range(8..<Int.max))
print(ex.selectedString) // HelloWorld!
NSAttributedString
您可以通过样式应用获取NSAttributedString
,它包含整个字符串(不含HTML标签)以及使用选择器选择的文本部分。库始终返回一个新的NSAttributedString
实例,因此可以直接使用,无需创建实例的副本。
let ex = "<span>Hello</span>, <span>World</span>!".ex
ex[.tag("span")].style(.color(.red))
// Get NSAttributedString containing the entire string
let attributedString1 = ex.attributedString
print(attributedString1.string) // Hello, World!
// Get NSAttributedString containing only the selected substring
let attributedString2 = ex.selectedAttributedString
print(attributedString2.string) // HelloWorld
// Get NSAttributedString containing only the selected substring using separator
let attributedString3 = ex.selectedAttributedString(separator: "-")
print(attributedString3.string) // Hello-World
操作
连接
您可以通过连接其他StringEx
实例、String
和NSAttributedString
来创建新的StringEx
实例。
let helloEx = "Hello".ex
let worldEx = "World".ex
let exclamationAttributed = NSAttributedString(string: "!", attributes: [.foregroundColor: UIColor.red])
let ex = helloEx + ", " + worldEx + exclamationAttributed
print(ex.rawString) // Hello, World!
替换
您可以使用其他StringEx
实例、String
或NSAttributedString
替换选定的子字符串。
如果使用HTML标签的选择器,则其工作结果是其内容的内部文字,因此替换只影响内部内容,并保留原始字符串中的标签。
let ex = "Hello, <span>World</span>!".ex
let str = ex[.tag("span")].replace(with: "Big World").rawString
print(str) // Hello, <span>Big World</span>!
在replace
方法中还有一个可选的mode
参数。该参数可以取两个值:.outer
(默认)/.inner
,并负责将HTML字符串中的所选范围转换为模式。以下示例显示了使用mode
参数时的区别
let ex = "Hello, <span><b></b></span>!".ex
let str1 = ex[.tag("span")].replace(with: "World", mode: .outer).rawString
let str2 = ex[.tag("span")].replace(with: "World", mode: .inner).rawString
print(str1) // Hello, <span>World</span>!
print(str2) // Hello, <span><b>World</b></span>!
附加
您可以使用append
方法将传递的StringEx
、String
或NSAttributedString
插入到字符串当前选择的每个子范围末尾。
let ex = "<span>Hello</span>, <span>World</span>!".ex
let str = ex[.tag("span")].append("?").rawString
print(str) // <span>Hello?</span>, <span>World?</span>!
前缀添加
您可以使用prepend
方法在当前选定的字符串子范围的开头插入传递的StringEx
、String
或NSAttributedString
。
let ex = "<span>Hello</span>, <span>World</span>!".ex
let str = ex[.tag("span")].prepend("?").rawString
print(str) // <span>?Hello</span>, <span>?World</span>!
插入
您可以使用insert
方法将传递的StringEx
、String
或NSAttributedString
插入到字符串每个当前选定的子范围的指定索引。
let ex = "<span>Hello</span>, <span>World</span>!".ex
let str = ex[.tag("span")].insert("?", at: 2).rawString
print(str) // <span>He?llo</span>, <span>Wo?rld</span>!
您也可以省略index
参数,将值插入到空的HTML标签中。
let ex = "Hello, <span />!".ex
let str = ex[.tag("span")].insert("World").rawString
print(str) // Hello, <span>World</span>!
样式
使用样式
您可以像这样对StringEx
实例的所选子字符串应用不同的样式。
let ex = "Hello, <span>World</span>!".ex
// Apply single style
ex[.tag("span")].style(.color(.red))
// or an array of styles
ex[.tag("span")].style([
.font(.systemFont(ofSize: 17.0)),
.color(.red),
.backgroundColor(.green)
])
以下样式可供使用:
样式 | 描述 |
---|---|
.font(_ font: UIFont) |
所选文本的字体 |
.color(_ color: UIColor) |
所选文本的颜色 |
.backgroundColor(_ color: UIColor) |
所选文本后面的背景区域的颜色 |
.kern(_ value: Double) |
调整字符间距的点的数量 |
.linkUrl(_ url: URL?) |
所选文本的链接 |
.linkString(_ string: String?) |
所选文本的链接 |
.shadow(_ shadow: NSShadow?) |
所选文本的阴影 |
.lineThroughStyle(_ style: NSUnderlineStyle, color: UIColor? = nil) |
线条贯穿的样式和颜色 |
.lineThroughStyles(_ styles: [NSUnderlineStyle], color: UIColor? = nil) |
线条贯穿的样式和颜色 |
.underlineStyle(_ style: NSUnderlineStyle, color: UIColor? = nil) |
下划线样式和颜色 |
.underlineStyles(_ styles: [NSUnderlineStyle], color: UIColor? = nil) |
下划线样式和颜色 |
.strokeWidth(_ width: Double, color: UIColor? = nil) |
所选文本的笔画 |
.baselineOffset(_ value: Double) |
字符与基线的偏移量,以点为单位 |
.paragraphStyle(_ value: NSParagraphStyle) |
段落属性 |
.aligment(_ value: NSTextAlignment) |
文本对齐方式 |
.firstLineHeadIndent(_ value: Double) |
首行缩进 |
.headIndent(_ value: Double) |
除首行外的行缩进 |
.tailIndent(_ value: Double) |
尾缩进 |
.lineHeightMultiple(_ value: Double) |
行高倍数 |
.lineSpacing(_ value: Double) |
行间间距,以点为单位 |
.paragraphSpacing(_ value: Double) |
段落结束后的空格 |
.paragraphSpacingBefore(_ value: Double) |
段落顶部和其文本内容开始的距离 |
使用
aligment
,firstLineHeadIndent
,headIndent
,tailIndent
,lineHeightMultiple
,lineSpacing
,paragraphSpacing
,paragraphSpacingBefore
样式,创建具有适当属性的NSParagraphStyle
对象。因此,重新应用这些样式之一将完全覆盖此对象。
清除样式
let ex = "Hello, <span>World</span>!".ex
// Apply style to entire string
ex.style(.color(.red))
// Clear styles for span tag
ex[.tag("span")].clearStyles()
样式表
您可以将常用样式存储在变量中,并将其应用于多个StringEx
实例,以避免代码重复。
let styles = [
Stylesheet(selector: .tag("b"), styles: [
.font(.boldSystemFont(ofSize: 24.0)),
.color(.black)
]),
Stylesheet(selector: .tag("em"), style: .font(.italicSystemFont(ofSize: 17.0)))
]
let ex1 = "Hello, <b>World</b>!".ex
let ex2 = "<em>Hello</em>, <b>World</b>!".ex
ex1.style(styles)
ex2.style(styles)
样式将按照它们在数组中列出的顺序应用。
样式管理器
样式管理器是一个主题分组集中式样式的中央存储库。样式管理器允许您自动将所选样式主题应用于StringEx
实例。
首先,您需要将一些样式添加到样式管理器中,并指定主题名称
// Set heading styles
StyleManager.shared.set("heading", [
Stylesheet(selector: .tag("h1"), styles: [
.font(.boldSystemFont(ofSize: 24.0)),
.color(.black)
]),
Stylesheet(selector: .tag("h2"), styles: [
.font(.boldSystemFont(ofSize: 18.0)),
.color(.gray)
])
])
// Set paragraph styles
// You can use subscript to set the styles
StyleManager.shared["paragraph"] = [
Stylesheet(selector: .tag("p"), styles: [
.font(.systemFont(ofSize: 17.0)),
.color(.black)
])
]
// You can combine multiple themes into one
StyleManager.shared.set("default", ["heading", "paragraph"])
然后,您可以设置或更改当前主题,如下所示
StyleManager.shared.use("default")
为了使特定的StringEx
实例使用样式管理器,您需要设置属性useStyleManager = true
let str = """
<h1>Page title</h1>
<p>Some text.</p>
<p>Some text.</p>
<h2>Title</h2>
<p>Some text.</p>
"""
let ex = str.ex
ex.useStyleManager = true
// After that, when you access the property to get the NSAttributedString,
// the styles will be automatically applied to it
myLabel.attributedText = ex.attributedString
许可协议
StringEx
是在MIT许可下发布的。有关更多信息,请参阅LICENSE文件。