aboutsummaryrefslogtreecommitdiff
path: root/iosApp/iosApp/Shared/HyperlinkText.swift
diff options
context:
space:
mode:
Diffstat (limited to 'iosApp/iosApp/Shared/HyperlinkText.swift')
-rw-r--r--iosApp/iosApp/Shared/HyperlinkText.swift126
1 files changed, 126 insertions, 0 deletions
diff --git a/iosApp/iosApp/Shared/HyperlinkText.swift b/iosApp/iosApp/Shared/HyperlinkText.swift
new file mode 100644
index 0000000..4b966eb
--- /dev/null
+++ b/iosApp/iosApp/Shared/HyperlinkText.swift
@@ -0,0 +1,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)
+ }
+}