# 扩展 String 的插值功能
在 Swift 中我们一般通过 \(Any)
的方式在 String 中插值,而在 Swift 5 中我们可以通过扩展 String.StringInterpolation
并实现带有不同参数的 appendInterpolation
方法来增强这一功能,这里以格式化输出时间为例
# 原始输出
print("\(Date())")
2020-03-18 07:15:51 +0000
# 格式化增强
/// 扩展 String.StringInterpolation
extension String.StringInterpolation {
/// 实现 appendInterpolation
/// 此处入参为 Date 类型,只对 Date 类型有效
mutating func appendInterpolation(_ value: Date) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm"
appendLiteral(formatter.string(from: value))
}
/// 可以带有多个参数
mutating func appendInterpolation(_ value: Date, formatterStr: String) {
let formatter = DateFormatter()
formatter.dateFormat = formatterStr
appendLiteral(formatter.string(from: value))
}
}
print("\(Date())")
print("\(Date(), formatterStr: "HH:mm:ss")")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2020-03-18 15:20 15:20:18
提示
通过在扩展中实现多个不同的 appendInterpolation
方法,我们就可以实现对不同数据进行格式化输出,方法中的 appendLiteral
将最终处理的结果添加到原始字符串上,如果遗漏则会变成插入空字符串
# 扩展自定义类型
实际上我们不止可以扩展原始的 String
, 通过遵循 ExpressibleByStringLiteral
与 ExpressibleByStringInterpolation
协议可以让自定义的类型具备通过字符串初始化并插值的功能
# 创建自定义类型
首先我们创建一个用来进行扩展的类型,例如构建一个可以通过插值方式生成属性字符串的新类型 AttributedString
/// 创建一个用来测试的类型,里面仅有一个属性
struct AttributedString {
let attributedString: NSAttributedString
}
2
3
4
现在 AttributedString
只是一个普通的结构体,接下来我们让它可以通过字符串初始化
# 遵循 ExpressibleByStringLiteral
协议
首先我们通过扩展方法遵循 ExpressibleByStringLiteral
协议并实现 ExpressibleByStringLiteral
协议中的初始化方法 init(stringLiteral: String)
,当我们自定义类在通过没有插值的字符串初始化时会执行这个方法来初始化
/// 实现协议并实现其中的初始化方法
extension AttributedString: ExpressibleByStringLiteral {
/// 没有插值的字符串会执行这个方法初始化
init(stringLiteral: String) {
attributedString = NSAttributedString(string: stringLiteral)
}
}
2
3
4
5
6
7
# 遵循 ExpressibleByStringInterpolation
协议
遵循 ExpressibleByStringInterpolation
协议需要我们实现其初始化方法 init(stringInterpolation: StringInterpolation)
,当使用带有插值的字符串初始化时会执行这个方法来初始化。同时我们还要添加一个类型为 StringInterpolation
的子类型并让其遵循 StringInterpolationProtocol
协议,这个类型用来对插值进行处理,初始化方法的参数就是这个类型
在 StringInterpolation
子类型中我们需要实现其初始化方法 init(literalCapacity: Int, interpolationCount: Int)
在这里可以进行初始化等操作,实现方法 appendLiteral(_ literal:String)
, 这个方法用来处理初始化字符串中非插值的部分,同时实现一个或多个不同的 appendInterpolation
方法,这个方法就如同上面扩展 Stirng 一样用来对不同类型的插值进行处理
在利用带有插值的字符串初始化时,会首先调用 StringInterpolation
子类型中的初始化方法,然后按字符串顺序依次解析,根据是否是插值以及插值的类型来调用 appendLiteral
和 appendInterpolation
方法,最后调用 init(stringInterpolation: StringInterpolation)
构建完成
/// 实现协议并实现其中的方法
extension AttributedString: ExpressibleByStringInterpolation {
/// `StringInterpolation` 子类型,用来处理插值
struct StringInterpolation: StringInterpolationProtocol {
/// 这里创建可变属性字符串组成最后的结果
let attributedString:NSMutableAttributedString
/// 实现初始化方法
init(literalCapacity: Int, interpolationCount: Int) {
attributedString = NSMutableAttributedString()
}
/// 实现 `appendLiteral` 方法,当遇到非插值部分时会调用该方法进行组合
func appendLiteral(_ literal:String) {
/// 把不是插值部分的字符串拼接到结果上
/// 此处可以对非插值部分进行处理
attributedString.append(NSAttributedString(string: literal))
}
/// 实现 `appendInterpolation` 方法,当遇到插值部分时会调用该方法进行处理
/// 可以实现多个来对不同类型的插值进行处理
func appendInterpolation(_ value: String) {
/// 添加插值部分到结果上
attributedString.append(NSAttributedString(string: value))
}
}
/// 插值字符串会执行该初始化方法
init(stringInterpolation: StringInterpolation) {
attributedString = NSAttributedString(attributedString: stringInterpolation.attributedString)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 使用
通过上面的几个步骤我们构建了一个简单的结构体类型 AttributedString
, 这个类型可以通过插值字符串初始化并可以返回一个属性字符串
let result: AttributedString = "测试\("插入一个值")"
print(result.attributedString)
2
通过添加更多的 appendInterpolation
方法可以处理更多不同类型的插值