subscript

subscript 是 Swift 中特有的一个方法。该方法可以让我们更方便的实现的一些功能。例如数组中,array[index],这样的实现其实都是通过 subscript 来实现。

那么,我们可以用 subscript 来干嘛呢?比如可以自定义 String 的 subscript,来达到更方便的切割字符串的功能。

String Extension

1
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
extension String {
/// 从开始位置截取到 index 位置
///
/// - Parameter index: 所要截取到的位置
/// - Returns: String
func substring(to idx: Int) -> String {
guard idx >= 0 else { return "" }
guard idx < count else { return self }
let endIndex = self.index(self.startIndex, offsetBy: idx)
return String(self[...endIndex])
}
/// 从 index 位置开始截取
///
/// - Parameter index: 所要开始截取的位置
/// - Returns: String
func substring(from idx: Int) -> String {
guard idx >= 0 else { return self }
guard idx < count else { return "" }
let startIndex = self.index(self.startIndex, offsetBy: idx)
return String(self[startIndex...])
}
/// eg: "swift"[r: 0..<3] // "swi"
///
/// - Parameter range: 开区间
subscript(r range: CountableRange<Int>) -> String {
guard range.lowerBound >= 0 else {
return substring(to: range.upperBound)
}
guard range.upperBound < count else {
return substring(from: range.lowerBound)
}
let startIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
let endIndex = self.index(self.startIndex, offsetBy: range.upperBound)
return String(self[startIndex..<endIndex])
}
/// eg: "swift"[r: 0...3] // "swif"
///
/// - Parameter range: 闭区间
subscript(r range: CountableClosedRange<Int>) -> String {
guard range.lowerBound >= 0 else {
return substring(to: range.upperBound)
}
guard range.upperBound < count else {
return substring(from: range.lowerBound)
}
let startIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
let endIndex = self.index(self.startIndex, offsetBy: range.upperBound)
return String(self[startIndex...endIndex])
}
/// eg: "swift"[r:1...] // “wift”
///
/// - Parameter range: PartialRangeFrom
subscript(r range: PartialRangeFrom<Int>) -> String {
guard range.lowerBound >= 0 else {
return self
}
let startIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
return String(self[startIndex...])
}
}

Array Extension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extension Array {
/// eg: [1,2][s: 4] // nil
///
/// - Parameter idx: index
subscript(s idx: Int) -> Element? {
guard 0..<count ~= idx else { return nil}
return self[idx]
}
}
extension Array where Element: Equatable {
/// eg: [1,2][e: 1] // 1
///
/// - Parameter element: element
subscript(e element: Element) -> Int? {
return firstIndex(of: element)
}
}

NSMutableAttributedString Extension

1
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
extension NSMutableAttributedString {
/// 检查是否越界
///
/// - Parameter range: range
private func check(_ range: inout NSRange) {
if range.location + range.length > string.count {
range = NSRange(location: range.location, length: string.count - range.location)
}
}
/// all
private var allRange : NSRange {
return NSRange(location: 0, length: string.count)
}
}
extension NSMutableAttributedString {
/// eg: attributedStr[.font] = UIFont.boldSystemFont(ofSize: 20)
subscript<T>(_ key: NSAttributedString.Key) -> T? {
get {
var range = allRange
return attribute(key, at: 0, effectiveRange: &range) as? T
}
set {
if let value = newValue {
let range = allRange
addAttribute(key, value: value, range: range)
}
}
}
}
// MARK: NSMutableAttributedString + Range
extension NSMutableAttributedString {
/// eg: attributedStr[.font, 0..<3] = UIFont.boldSystemFont(ofSize: 20)
subscript<T>(_ key: NSAttributedString.Key, r: Range<Int>) -> T? {
get {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound)
check(&range)
return attribute(key, at: 0, effectiveRange: &range) as? T
}
set {
if let value = newValue {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound)
check(&range)
addAttribute(key, value: value, range: range)
}
}
}
subscript<T>(_ key: NSAttributedString.Key, r: Range<String.Index>) -> T? {
get {
let lowerBound = r.lowerBound.encodedOffset
let upperBound = r.upperBound.encodedOffset
var range = NSRange(location: lowerBound, length: upperBound - lowerBound)
check(&range)
return attribute(key, at: 0, effectiveRange: &range) as? T
}
set {
if let value = newValue {
let lowerBound = r.lowerBound.encodedOffset
let upperBound = r.upperBound.encodedOffset
var range = NSRange(location: lowerBound, length: upperBound - lowerBound)
check(&range)
addAttribute(key, value: value, range: range)
}
}
}
/// eg: attributedStr[0..<3] = [.font: UIFont.boldSystemFont(ofSize: 20)]
subscript<T>(_ r: Range<Int>) -> [NSAttributedString.Key: T]? {
get {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound)
check(&range)
var dict : [NSAttributedString.Key: T] = [:]
enumerateAttributes(in: range, options: .longestEffectiveRangeNotRequired) { (atts, range, pointer) in
dict = atts as! [NSAttributedString.Key : T]
}
return dict
}
set {
if let value = newValue {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound)
check(&range)
value.forEach {
addAttribute($0, value: $1, range: range)
}
}
}
}
}
// MARK: NSMutableAttributedString + ClosedRange
extension NSMutableAttributedString {
/// eg: attributedStr[.font, 0...3] = UIFont.boldSystemFont(ofSize: 20)
subscript<T>(_ key: NSAttributedString.Key, r: ClosedRange<Int>) -> T? {
get {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound + 1)
check(&range)
return attribute(key, at: 0, effectiveRange: &range) as? T
}
set {
if let value = newValue {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound + 1)
check(&range)
addAttribute(key, value: value, range: range)
}
}
}
/// eg: attributedStr[0...3] = [.font: UIFont.boldSystemFont(ofSize: 20)]
subscript<T>(_ r: ClosedRange<Int>) -> [NSAttributedString.Key: T]? {
get {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound + 1)
check(&range)
var dict : [NSAttributedString.Key: T] = [:]
enumerateAttributes(in: range, options: .longestEffectiveRangeNotRequired) { (atts, range, pointer) in
dict = atts as! [NSAttributedString.Key : T]
}
return dict
}
set {
if let value = newValue {
var range = NSRange(location: r.lowerBound, length: r.upperBound - r.lowerBound + 1)
check(&range)
value.forEach {
addAttribute($0, value: $1, range: range)
}
}
}
}
}
// MARK: NSMutableAttributedString + PartialRangeFrom
extension NSMutableAttributedString {
/// eg: attributedStr[.font, 3...] = UIFont.boldSystemFont(ofSize: 20)
subscript<T>(_ key: NSAttributedString.Key, r: PartialRangeFrom<Int>) -> T? {
get {
var range = NSRange(location: r.lowerBound, length: string.count - r.lowerBound)
check(&range)
return attribute(key, at: 0, effectiveRange: &range) as? T
}
set {
if let value = newValue {
var range = NSRange(location: r.lowerBound, length: string.count - r.lowerBound)
check(&range)
addAttribute(key, value: value, range: range)
}
}
}
/// eg: attributedStr[3...] = [.font: UIFont.boldSystemFont(ofSize: 20)]
subscript<T>(_ r: PartialRangeFrom<Int>) -> [NSAttributedString.Key: T]? {
get {
var range = NSRange(location: r.lowerBound, length: string.count - r.lowerBound)
check(&range)
var dict : [NSAttributedString.Key: T] = [:]
enumerateAttributes(in: range, options: .longestEffectiveRangeNotRequired) { (atts, range, pointer) in
dict = atts as! [NSAttributedString.Key : T]
}
return dict
}
set {
if let value = newValue {
var range = NSRange(location: r.lowerBound, length: string.count - r.lowerBound)
check(&range)
value.forEach {
addAttribute($0, value: $1, range: range)
}
}
}
}
}

总结

上述 extension 的具体用法,看注释吧。

又水了一篇~(逃