Last active
December 19, 2024 16:55
-
-
Save B0Y3R/824a64d5647bfb15274774ba81b664b8 to your computer and use it in GitHub Desktop.
Inline clickable highlightable text in swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import SwiftUI | |
struct Highlight { | |
let text: String | |
let data: String | |
let onClick: (String) -> Void | |
} | |
struct HighlightedText: View { | |
private struct TextData { | |
let text: String | |
let tag: String? | |
let data: String? | |
let onClick: ((String) -> Void)? | |
} | |
let text: String | |
let highlights: [Highlight] | |
let infoTextColor: Color | |
let infoLinkTextColor: Color | |
let textStyle: Font // Simplified from TextStyle for this example | |
var body: some View { | |
let textData = processTextData() | |
Text(attributedString(from: textData)) | |
} | |
private func processTextData() -> [TextData] { | |
var textData: [TextData] = [] | |
if highlights.isEmpty { | |
textData.append(TextData( | |
text: text, | |
tag: nil, | |
data: nil, | |
onClick: nil | |
)) | |
return textData | |
} | |
var startIndex = text.startIndex | |
for (i, highlight) in highlights.enumerated() { | |
guard let endIndex = text.range(of: highlight.text)?.lowerBound else { | |
fatalError("Highlighted text mismatch") | |
} | |
// Add text before highlight | |
if startIndex < endIndex { | |
textData.append(TextData( | |
text: String(text[startIndex..<endIndex]), | |
tag: nil, | |
data: nil, | |
onClick: nil | |
)) | |
} | |
// Add highlighted text | |
textData.append(TextData( | |
text: highlight.text, | |
tag: "\(highlight.text)_TAG", | |
data: highlight.data, | |
onClick: highlight.onClick | |
)) | |
startIndex = text.index(endIndex, offsetBy: highlight.text.count) | |
// Add remaining text after last highlight | |
if i == highlights.count - 1 && startIndex < text.endIndex { | |
textData.append(TextData( | |
text: String(text[startIndex...]), | |
tag: nil, | |
data: nil, | |
onClick: nil | |
)) | |
} | |
} | |
return textData | |
} | |
private func attributedString(from textData: [TextData]) -> AttributedString { | |
var attributedString = AttributedString("") | |
for data in textData { | |
var portion = AttributedString(data.text) | |
if data.tag != nil && data.data != nil { | |
portion.foregroundColor = infoLinkTextColor | |
portion.link = URL(string: "action://\(data.data!)") | |
} else { | |
portion.foregroundColor = infoTextColor | |
} | |
attributedString += portion | |
} | |
return attributedString | |
} | |
} | |
#Preview { | |
VStack(spacing: 20) { | |
// Simple example | |
HighlightedText( | |
text: "Click here to learn more about our Terms and Conditions", | |
highlights: [ | |
Highlight( | |
text: "Terms and Conditions", | |
data: "terms", | |
onClick: { data in | |
print("Clicked terms: \(data)") | |
} | |
) | |
], | |
infoTextColor: .black, | |
infoLinkTextColor: .blue, | |
textStyle: .body | |
) | |
// Multiple highlights example | |
HighlightedText( | |
text: "Please review our Privacy Policy and Terms of Service before continuing", | |
highlights: [ | |
Highlight( | |
text: "Privacy Policy", | |
data: "privacy", | |
onClick: { data in | |
print("Clicked privacy: \(data)") | |
} | |
), | |
Highlight( | |
text: "Terms of Service", | |
data: "terms", | |
onClick: { data in | |
print("Clicked terms: \(data)") | |
} | |
) | |
], | |
infoTextColor: .gray, | |
infoLinkTextColor: .blue, | |
textStyle: .callout | |
) | |
// Different styling example | |
HighlightedText( | |
text: "Contact Support for help", | |
highlights: [ | |
Highlight( | |
text: "Support", | |
data: "support", | |
onClick: { data in | |
print("Clicked support: \(data)") | |
} | |
) | |
], | |
infoTextColor: .black, | |
infoLinkTextColor: .green, | |
textStyle: .title3 | |
) | |
} | |
.padding() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment