/** * TrackerMap * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import SwiftUI import SwiftUIFlowLayout // Source: https://swiftuirecipes.com/blog/hyperlinks-in-swiftui-text 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) } }