Skip to content

Commit 3546995

Browse files
committed
增加数组去重扩展, 优化匹配效率, 移除无用代码
1 parent ae3e7df commit 3546995

File tree

5 files changed

+137
-83
lines changed

5 files changed

+137
-83
lines changed

AttributedString.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
9B8765A124850742009C51C2 /* ObjectExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B87659F24850742009C51C2 /* ObjectExtension.swift */; };
7474
9B8765A224850742009C51C2 /* ObjectExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B87659F24850742009C51C2 /* ObjectExtension.swift */; };
7575
9B8765A324850742009C51C2 /* ObjectExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B87659F24850742009C51C2 /* ObjectExtension.swift */; };
76+
9BA451E824B0412E00BE3F02 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA451E724B0412E00BE3F02 /* ArrayExtension.swift */; };
77+
9BA451E924B0412E00BE3F02 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA451E724B0412E00BE3F02 /* ArrayExtension.swift */; };
78+
9BA451EA24B0412E00BE3F02 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA451E724B0412E00BE3F02 /* ArrayExtension.swift */; };
79+
9BA451EB24B0412E00BE3F02 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BA451E724B0412E00BE3F02 /* ArrayExtension.swift */; };
7680
9BAA0DCD24A4915F00702C23 /* Checking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BBC525E24A053A600CBDFFF /* Checking.swift */; };
7781
9BAA0DCE24A4916A00702C23 /* Checking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BBC525E24A053A600CBDFFF /* Checking.swift */; };
7882
9BAA0DCF24A4916A00702C23 /* Checking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BBC525E24A053A600CBDFFF /* Checking.swift */; };
@@ -140,6 +144,7 @@
140144
9B6E89E22382C392009EBEBE /* ParagraphStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParagraphStyle.swift; sourceTree = "<group>"; };
141145
9B6E89E42382C488009EBEBE /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
142146
9B87659F24850742009C51C2 /* ObjectExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectExtension.swift; sourceTree = "<group>"; };
147+
9BA451E724B0412E00BE3F02 /* ArrayExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtension.swift; sourceTree = "<group>"; };
143148
9BBC525E24A053A600CBDFFF /* Checking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checking.swift; sourceTree = "<group>"; };
144149
9BE7D4C52488E0FC00DE1176 /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
145150
/* End PBXFileReference section */
@@ -271,6 +276,7 @@
271276
9B0A2A3723851EB6003D8A55 /* Extension.swift */,
272277
9B87659F24850742009C51C2 /* ObjectExtension.swift */,
273278
9B6E89DE23828F7C009EBEBE /* ShadowExtension.swift */,
279+
9BA451E724B0412E00BE3F02 /* ArrayExtension.swift */,
274280
);
275281
path = Extension;
276282
sourceTree = "<group>";
@@ -603,6 +609,7 @@
603609
9B267B8C24406B2F002F571E /* WKInterfaceLabelExtension.swift in Sources */,
604610
9B267B77244066C1002F571E /* CGSizeExtension.swift in Sources */,
605611
9B267B78244066C1002F571E /* CGRectExtension.swift in Sources */,
612+
9BA451EA24B0412E00BE3F02 /* ArrayExtension.swift in Sources */,
606613
9B267B79244066C1002F571E /* CGPointExtension.swift in Sources */,
607614
9BE7D4C82488E0FC00DE1176 /* Action.swift in Sources */,
608615
9BAA0DCF24A4916A00702C23 /* Checking.swift in Sources */,
@@ -632,6 +639,7 @@
632639
9B267BB32440811F002F571E /* CGSizeExtension.swift in Sources */,
633640
9B267BB42440811F002F571E /* CGRectExtension.swift in Sources */,
634641
9BE7D4C92488E0FC00DE1176 /* Action.swift in Sources */,
642+
9BA451EB24B0412E00BE3F02 /* ArrayExtension.swift in Sources */,
635643
9B267BB52440811F002F571E /* CGPointExtension.swift in Sources */,
636644
9B267BB62440811F002F571E /* Extension.swift in Sources */,
637645
9B267BB72440811F002F571E /* AttributedString.swift in Sources */,
@@ -661,6 +669,7 @@
661669
9BAA0DCE24A4916A00702C23 /* Checking.swift in Sources */,
662670
9B34BD42243DBEA900932E6C /* CGPointExtension.swift in Sources */,
663671
9B34BD43243DBEA900932E6C /* Extension.swift in Sources */,
672+
9BA451E924B0412E00BE3F02 /* ArrayExtension.swift in Sources */,
664673
9B6854C524971B8B005F301C /* ShadowExtension.swift in Sources */,
665674
9B34BD44243DBEA900932E6C /* AttributedString.swift in Sources */,
666675
9B34BD45243DBEA900932E6C /* Interpolation.swift in Sources */,
@@ -685,6 +694,7 @@
685694
9B6E89DF23828F7C009EBEBE /* ShadowExtension.swift in Sources */,
686695
9B6E89AF238275D2009EBEBE /* CGSizeExtension.swift in Sources */,
687696
9BE7D4C62488E0FC00DE1176 /* Action.swift in Sources */,
697+
9BA451E824B0412E00BE3F02 /* ArrayExtension.swift in Sources */,
688698
9B6E89B12382766F009EBEBE /* CGRectExtension.swift in Sources */,
689699
9B6E89A8238273E4009EBEBE /* UITextViewExtension.swift in Sources */,
690700
9B0A2A3823851EB6003D8A55 /* Extension.swift in Sources */,

Sources/Action.swift

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -241,35 +241,6 @@ extension AttributedString.Action.Trigger {
241241
}
242242
}
243243

244-
extension AttributedString {
245-
246-
/// 设置Action
247-
/// - Parameters:
248-
/// - range: 范围
249-
/// - action: Action
250-
mutating func set(range: NSRange, action: Action) {
251-
let string = NSMutableAttributedString(attributedString: value)
252-
string.addAttribute(.action, value: action, range: range)
253-
value = string
254-
}
255-
256-
/// 添加Action (如果该范围内已存在Action则不再添加 防止覆盖)
257-
/// - Parameters:
258-
/// - range: 范围
259-
/// - action: Action
260-
@discardableResult
261-
mutating func add(range: NSRange, action: Action) -> Bool {
262-
guard value.attribute(.action, at: range.location, effectiveRange: nil) == nil else {
263-
return false
264-
}
265-
266-
let string = NSMutableAttributedString(attributedString: value)
267-
string.addAttribute(.action, value: action, range: range)
268-
value = string
269-
return true
270-
}
271-
}
272-
273244
extension NSAttributedString {
274245

275246
func get(_ range: NSRange) -> AttributedString.Action.Result {

Sources/Checking.swift

Lines changed: 73 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,38 @@ import UIKit
2020
extension AttributedString {
2121

2222
public enum Checking: Hashable {
23+
/// 自定义范围
24+
case range(NSRange)
25+
/// 正则表达式
26+
case regex(String)
2327
#if os(iOS) || os(macOS)
2428
case action
2529
#endif
30+
///
2631
case date
2732
case link
2833
case address
2934
case phoneNumber
3035
case transitInformation
31-
case regex(String)
3236
}
3337
}
3438

3539
extension AttributedString.Checking {
3640

3741
public enum Result {
42+
/// 自定义范围
43+
case range(NSAttributedString)
44+
/// 正则表达式
45+
case regex(NSAttributedString)
3846
#if os(iOS) || os(macOS)
3947
case action(AttributedString.Action.Result)
4048
#endif
49+
4150
case date(Date)
4251
case link(URL)
4352
case address(Address)
4453
case phoneNumber(String)
4554
case transitInformation(TransitInformation)
46-
47-
case regex(String)
4855
}
4956
}
5057

@@ -89,45 +96,41 @@ public extension Array where Element == AttributedString.Checking {
8996
extension AttributedString {
9097

9198
public mutating func add(attributes: [Attribute], checkings: [Checking] = .defalut) {
99+
guard !attributes.isEmpty, !checkings.isEmpty else { return }
100+
92101
var temp: [NSAttributedString.Key: Any] = [:]
93102
attributes.forEach { temp.merge($0.attributes, uniquingKeysWith: { $1 }) }
94103

95104
let matched = matching(checkings)
96-
97105
let string = NSMutableAttributedString(attributedString: value)
98-
matched.forEach {
99-
string.addAttributes(temp, range: $0.0)
100-
}
101-
106+
matched.forEach { string.addAttributes(temp, range: $0.0) }
102107
value = string
103108
}
104109

105110
public mutating func set(attributes: [Attribute], checkings: [Checking] = .defalut) {
111+
guard !attributes.isEmpty, !checkings.isEmpty else { return }
112+
106113
var temp: [NSAttributedString.Key: Any] = [:]
107114
attributes.forEach { temp.merge($0.attributes, uniquingKeysWith: { $1 }) }
108115

109116
let matched = matching(checkings)
110-
111117
let string = NSMutableAttributedString(attributedString: value)
112-
matched.forEach {
113-
string.setAttributes(temp, range: $0.0)
114-
}
115-
118+
matched.forEach { string.setAttributes(temp, range: $0.0) }
116119
value = string
117120
}
118121
}
119122

120123
extension AttributedString {
121124

122-
/// 匹配检查 (Range 不会出现覆盖情况, 优先级 action > regex > other)
125+
/// 匹配检查 (Key 不会出现覆盖情况, 优先级 range > action > regex > other)
123126
/// - Parameter checkings: 检查类型
124127
/// - Returns: 匹配结果 (范围, 检查类型, 检查结果)
125128
func matching(_ checkings: [Checking]) -> [NSRange: (Checking, Checking.Result)] {
126129
guard !checkings.isEmpty else {
127130
return [:]
128131
}
129132

130-
let checkings = Set(checkings)
133+
let checkings = checkings.filtered(duplication: \.self).sorted { $0.order < $1.order }
131134
var result: [NSRange: (Checking, Checking.Result)] = [:]
132135

133136
func contains(_ range: NSRange) -> Bool {
@@ -140,53 +143,69 @@ extension AttributedString {
140143
return result.keys.contains(where: { $0.overlap(range) })
141144
}
142145

143-
// Actions
144-
#if os(iOS) || os(macOS)
145-
if checkings.contains(.action) {
146-
let actions: [NSRange: AttributedString.Action] = value.get(.action)
147-
for action in actions where !contains(action.key) {
148-
result[action.key] = (.action, .action(value.get(action.key)))
149-
}
150-
}
151-
#endif
152-
153-
// 正则表达式
154146
checkings.forEach { (checking) in
155-
guard case .regex(let string) = checking else { return }
156-
guard let regex = try? NSRegularExpression(pattern: string, options: .caseInsensitive) else { return }
157-
158-
let matches = regex.matches(
159-
in: value.string,
160-
options: .init(),
161-
range: .init(location: 0, length: value.length)
162-
)
163-
164-
for match in matches where !contains(match.range) {
165-
let substring = value.attributedSubstring(from: match.range)
166-
result[match.range] = (checking, .regex(substring.string))
167-
}
168-
}
169-
170-
// 数据检测器
171-
if let detector = try? NSDataDetector(types: NSTextCheckingAllTypes) {
172-
let matches = detector.matches(
173-
in: value.string,
174-
options: .init(),
175-
range: .init(location: 0, length: value.length)
176-
)
177-
178-
for match in matches where !contains(match.range) {
179-
guard let type = match.resultType.map() else { continue }
180-
guard checkings.contains(type) else { continue }
181-
guard let mapped = match.map() else { continue }
182-
result[match.range] = (type, mapped)
147+
switch checking {
148+
case .range(let range) where !contains(range):
149+
let substring = value.attributedSubstring(from: range)
150+
result[range] = (checking, .range(substring))
151+
152+
case .regex(let string):
153+
guard let regex = try? NSRegularExpression(pattern: string, options: .caseInsensitive) else { return }
154+
155+
let matches = regex.matches(
156+
in: value.string,
157+
options: .init(),
158+
range: .init(location: 0, length: value.length)
159+
)
160+
161+
for match in matches where !contains(match.range) {
162+
let substring = value.attributedSubstring(from: match.range)
163+
result[match.range] = (checking, .regex(substring))
164+
}
165+
166+
case .action:
167+
let actions: [NSRange: AttributedString.Action] = value.get(.action)
168+
for action in actions where !contains(action.key) {
169+
result[action.key] = (.action, .action(value.get(action.key)))
170+
}
171+
172+
case .date, .link, .address, .phoneNumber, .transitInformation:
173+
guard let detector = try? NSDataDetector(types: NSTextCheckingAllTypes) else { return }
174+
175+
let matches = detector.matches(
176+
in: value.string,
177+
options: .init(),
178+
range: .init(location: 0, length: value.length)
179+
)
180+
181+
for match in matches where !contains(match.range) {
182+
guard let type = match.resultType.map() else { continue }
183+
guard checkings.contains(type) else { continue }
184+
guard let mapped = match.map() else { continue }
185+
result[match.range] = (type, mapped)
186+
}
187+
188+
default:
189+
break
183190
}
184191
}
185192

186193
return result
187194
}
188195
}
189196

197+
fileprivate extension AttributedString.Checking {
198+
199+
var order: Int {
200+
switch self {
201+
case .range: return 0
202+
case .regex: return 1
203+
case .action: return 2
204+
default: return 3
205+
}
206+
}
207+
}
208+
190209
fileprivate extension AttributedString.Checking {
191210

192211
func map() -> NSTextCheckingResult.CheckingType? {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// ArrayExtension.swift
3+
// ┌─┐ ┌───────┐ ┌───────┐
4+
// │ │ │ ┌─────┘ │ ┌─────┘
5+
// │ │ │ └─────┐ │ └─────┐
6+
// │ │ │ ┌─────┘ │ ┌─────┘
7+
// │ └─────┐│ └─────┐ │ └─────┐
8+
// └───────┘└───────┘ └───────┘
9+
//
10+
// Created by Lee on 2020/7/4.
11+
// Copyright © 2020 LEE. All rights reserved.
12+
//
13+
14+
extension Array {
15+
16+
/// 过滤重复元素
17+
/// - Parameter path: KeyPath条件
18+
func filtered<E: Equatable>(duplication path: KeyPath<Element, E>) -> [Element] {
19+
return reduce(into: [Element]()) { (result, e) in
20+
let contains = result.contains { $0[keyPath: path] == e[keyPath: path] }
21+
result += contains ? [] : [e]
22+
}
23+
}
24+
25+
/// 过滤重复元素
26+
/// - Parameter closure: 过滤条件
27+
func filtered<E: Equatable>(duplication closure: (Element) throws -> E) rethrows -> [Element] {
28+
return try reduce(into: [Element]()) { (result, e) in
29+
let contains = try result.contains { try closure($0) == closure(e) }
30+
result += contains ? [] : [e]
31+
}
32+
}
33+
34+
/// 过滤重复元素
35+
/// - Parameter path: KeyPath条件
36+
@discardableResult
37+
mutating func filter<E: Equatable>(duplication path: KeyPath<Element, E>) -> [Element] {
38+
self = filtered(duplication: path)
39+
return self
40+
}
41+
42+
/// 过滤重复元素
43+
/// - Parameter closure: 过滤条件
44+
@discardableResult
45+
mutating func filter<E: Equatable>(duplication closure: (Element) throws -> E) rethrows -> [Element] {
46+
self = try filtered(duplication: closure)
47+
return self
48+
}
49+
}
50+
51+
extension Array where Element: Equatable {
52+
53+
}

Sources/Extension/ShadowExtension.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// Created by Lee on 2019/11/18.
1111
// Copyright © 2019 LEE. All rights reserved.
1212
//
13+
1314
#if os(iOS) || os(tvOS)
1415
import UIKit
1516
#elseif os(macOS)

0 commit comments

Comments
 (0)