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
|
// Source: https://swiftuirecipes.com/blog/hyperlinks-in-swiftui-text
import SwiftUI
import SwiftUIFlowLayout
struct HyperlinkText: View {
private let pairs: [StringWithAttributes]
init(_ attributedString: NSAttributedString) {
pairs = attributedString.stringsWithAttributes
}
init?(html: String) {
if let data = html.data(using: .utf8),
let attributedString = try? NSAttributedString(data: data,
options: [.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil) {
self.init(attributedString)
} else {
return nil
}
}
var body: some View {
FlowLayout(mode: .vstack,
binding: .constant(false),
items: pairs,
itemSpacing: 0) { pair in
if let link = pair.attrs[.link],
let url = link as? URL {
Text(pair)
.onTapGesture {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
} else {
Text(pair)
}
}
}
}
struct StringWithAttributes: Hashable, Identifiable {
let id = UUID()
let string: String
let attrs: [NSAttributedString.Key: Any]
static func == (lhs: StringWithAttributes, rhs: StringWithAttributes) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
extension NSAttributedString {
var stringsWithAttributes: [StringWithAttributes] {
var attributes = [StringWithAttributes]()
enumerateAttributes(in: NSRange(location: 0, length: length), options: []) { (attrs, range, _) in
let string = attributedSubstring(from: range).string
attributes.append(StringWithAttributes(string: string, attrs: attrs))
}
return attributes
}
}
extension Text {
init(_ singleAttribute: StringWithAttributes) {
let string = singleAttribute.string
let attrs = singleAttribute.attrs
var text = Text(string)
/*if let font = attrs[.font] as? UIFont {
text = text.font(.init(font))
}
if let color = attrs[.foregroundColor] as? UIColor {
text = text.foregroundColor(Color(color))
}*/
if let kern = attrs[.kern] as? CGFloat {
text = text.kerning(kern)
}
if #available(iOS 14.0, *) {
if let tracking = attrs[.tracking] as? CGFloat {
text = text.tracking(tracking)
}
}
if let strikethroughStyle = attrs[.strikethroughStyle] as? NSNumber, strikethroughStyle != 0 {
if let strikethroughColor = (attrs[.strikethroughColor] as? UIColor) {
text = text.strikethrough(true, color: Color(strikethroughColor))
} else {
text = text.strikethrough(true)
}
}
if let underlineStyle = attrs[.underlineStyle] as? NSNumber,
underlineStyle != 0 {
if let underlineColor = (attrs[.underlineColor] as? UIColor) {
text = text.underline(true, color: Color(underlineColor))
} else {
text = text.underline(true)
}
}
if let baselineOffset = attrs[.baselineOffset] as? NSNumber {
text = text.baselineOffset(CGFloat(baselineOffset.floatValue))
}
self = text
}
init(_ attributes: [StringWithAttributes]) {
self.init("")
for singleAttribute in attributes {
self = self + Text(singleAttribute)
}
}
init(_ attributedString: NSAttributedString) {
self.init(attributedString.stringsWithAttributes)
}
}
|