SwiftUI Text with Clickable Links
In this article, I will show you how to create a custom SwiftUI component TextWithClickableLinks
, that finds and makes any links clickable inside the given text input.
Note: This implementation uses AttributedString, which requires iOS15+
Detecting URLs
In a helper file DetectURLs.swift
, create a function detectURLs
with the following code:
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">SwiftUI</span></span>
<span></span>
<span><span style="color: var(--shiki-token-comment)">/// Detects URLs in the given text and splits it into parts.</span></span>
<span><span style="color: var(--shiki-token-keyword)">func</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">detectURLs</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-function)">in</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-parameter)">text</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-constant)">String</span><span style="color: var(--shiki-color-text)">) </span><span style="color: var(--shiki-token-keyword)">-></span><span style="color: var(--shiki-color-text)"> [(</span><span style="color: var(--shiki-token-constant)">String</span><span style="color: var(--shiki-color-text)">, URL</span><span style="color: var(--shiki-token-keyword)">?</span><span style="color: var(--shiki-color-text)">)] {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> types: NSTextCheckingResult.CheckingType </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> .link</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> detector </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">try?</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">NSDataDetector</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">types</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> types.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> matches </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> detector</span><span style="color: var(--shiki-token-keyword)">?</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-function)">matches</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">in</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> text, options</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> [], range</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> NSRange</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">location</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-function)">, length</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> text.</span><span style="color: var(--shiki-token-constant)">utf16</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">count</span><span style="color: var(--shiki-token-punctuation)">))</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">??</span><span style="color: var(--shiki-color-text)"> []</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> results: [(</span><span style="color: var(--shiki-token-constant)">String</span><span style="color: var(--shiki-color-text)">, URL</span><span style="color: var(--shiki-token-keyword)">?</span><span style="color: var(--shiki-color-text)">)] </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> []</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> lastIndex </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> text.</span><span style="color: var(--shiki-token-constant)">startIndex</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">for</span><span style="color: var(--shiki-color-text)"> match </span><span style="color: var(--shiki-token-keyword)">in</span><span style="color: var(--shiki-color-text)"> matches {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> range </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">Range</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">match.</span><span style="color: var(--shiki-token-constant)">range</span><span style="color: var(--shiki-token-function)">, in</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> text</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> range.lowerBound </span><span style="color: var(--shiki-token-keyword)">></span><span style="color: var(--shiki-color-text)"> lastIndex {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> nonLinkText </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">String</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">text</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-token-function)">lastIndex</span><span style="color: var(--shiki-token-keyword)">..<</span><span style="color: var(--shiki-token-function)">range.lowerBound</span><span style="color: var(--shiki-token-punctuation)">])</span></span>
<span><span style="color: var(--shiki-color-text)"> results.</span><span style="color: var(--shiki-token-function)">append</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">(nonLinkText, </span><span style="color: var(--shiki-token-constant)">nil</span><span style="color: var(--shiki-token-function)">)</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> linkText </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">String</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">text</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-token-function)">range</span><span style="color: var(--shiki-token-punctuation)">])</span></span>
<span><span style="color: var(--shiki-color-text)"> results.</span><span style="color: var(--shiki-token-function)">append</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">(linkText, match.</span><span style="color: var(--shiki-token-constant)">url</span><span style="color: var(--shiki-token-function)">)</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> lastIndex </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> range.upperBound</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> lastIndex </span><span style="color: var(--shiki-token-keyword)"><</span><span style="color: var(--shiki-color-text)"> text.</span><span style="color: var(--shiki-token-constant)">endIndex</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> remainingText </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">String</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">text</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-token-function)">lastIndex</span><span style="color: var(--shiki-token-keyword)">..<</span><span style="color: var(--shiki-token-function)">text.</span><span style="color: var(--shiki-token-constant)">endIndex</span><span style="color: var(--shiki-token-punctuation)">])</span></span>
<span><span style="color: var(--shiki-color-text)"> results.</span><span style="color: var(--shiki-token-function)">append</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">(remainingText, </span><span style="color: var(--shiki-token-constant)">nil</span><span style="color: var(--shiki-token-function)">)</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> results</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
This function will detect any URLs in the text input and returns a list containing the original string, split up in parts:
<span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> input </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Visit isak.me for more posts on SwiftUI!"</span></span>
<span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> output </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> [(</span><span style="color: var(--shiki-token-string-expression)">"Visit "</span><span style="color: var(--shiki-color-text)">, </span><span style="color: var(--shiki-token-constant)">nil</span><span style="color: var(--shiki-color-text)">), (</span><span style="color: var(--shiki-token-string-expression)">"isak.me"</span><span style="color: var(--shiki-color-text)">, </span><span style="color: var(--shiki-token-constant)">Optional</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-string-expression)">"http://isak.me"</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)">), (</span><span style="color: var(--shiki-token-string-expression)">" for more posts on SwiftUI!"</span><span style="color: var(--shiki-color-text)">, </span><span style="color: var(--shiki-token-constant)">nil</span><span style="color: var(--shiki-color-text)">)]</span></span>
<span></span>
Rendering the text and styling the links
To create a view rendering the text with stylized and clickable links, we utilize AttributedString. AttributedString
is a value type for a string with associated attributes for portions of its text. We create a computed variable attributedString
, which uses our previously created detectURLs
function, and loops through the result, giving appropriate attribute to any links found along the way.
<span><span style="color: var(--shiki-token-keyword)">private</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> attributedString: AttributedString {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> attributedString </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">AttributedString</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">text</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> parts </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">detectURLs</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">in</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> text</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">for</span><span style="color: var(--shiki-color-text)"> part </span><span style="color: var(--shiki-token-keyword)">in</span><span style="color: var(--shiki-color-text)"> parts {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> url </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> part.1, </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> range </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> attributedString.</span><span style="color: var(--shiki-token-constant)">range</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">of</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> part.0</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> attributedString</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-color-text)">range</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)">.link </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> url</span></span>
<span><span style="color: var(--shiki-color-text)"> attributedString</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-color-text)">range</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)">.foregroundColor </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> .blue</span></span>
<span><span style="color: var(--shiki-color-text)"> attributedString</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-color-text)">range</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)">.underlineStyle </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> .single</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> attributedString</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
This attributedString
variable can be rendered in a normal Text
view.
<span><span style="color: var(--shiki-token-function)">Text</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">attributedString</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span></span>
Usage
I’ve added maxFrameWidth
and padding
to the component to allow for more more flexible styling of the view.
The entire component ends up looking like the following:
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">SwiftUI</span></span>
<span></span>
<span><span style="color: var(--shiki-token-comment)">/// A view that displays text with clickable links.</span></span>
<span><span style="color: var(--shiki-token-comment)">/// Clickable links are only supported on iOS 15 and later.</span></span>
<span><span style="color: var(--shiki-token-keyword)">struct</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">TextWithClickableLinks</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">View </span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> text: </span><span style="color: var(--shiki-token-constant)">String</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> customFont: Font</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> multilineTextAlignment: TextAlignment</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> maxFrameWidth: CGFloat</span><span style="color: var(--shiki-token-keyword)">?</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> padding: CGFloat</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> body: </span><span style="color: var(--shiki-token-keyword)">some</span><span style="color: var(--shiki-color-text)"> View {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Text</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">attributedString</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">multilineTextAlignment</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">multilineTextAlignment</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">frame</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">maxWidth</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> maxFrameWidth</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">padding</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">padding</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">private</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> attributedString: AttributedString {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> attributedString </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">AttributedString</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">text</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> parts </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">detectURLs</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">in</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> text</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">for</span><span style="color: var(--shiki-color-text)"> part </span><span style="color: var(--shiki-token-keyword)">in</span><span style="color: var(--shiki-color-text)"> parts {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> url </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> part.1, </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> range </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> attributedString.</span><span style="color: var(--shiki-token-constant)">range</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">of</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> part.0</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> attributedString</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-color-text)">range</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)">.link </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> url</span></span>
<span><span style="color: var(--shiki-color-text)"> attributedString</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-color-text)">range</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)">.foregroundColor </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> .blue</span></span>
<span><span style="color: var(--shiki-color-text)"> attributedString</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-color-text)">range</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)">.underlineStyle </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> .single</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> attributedString</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
<span><span style="color: var(--shiki-token-function)">#Preview</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">TextWithClickableLinks</span><span style="color: var(--shiki-token-punctuation)">(</span></span>
<span><span style="color: var(--shiki-token-function)"> text</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> </span><span style="color: var(--shiki-token-string-expression)">"Visit isak.me/blog for more posts on SwiftUI!"</span><span style="color: var(--shiki-token-function)">,</span></span>
<span><span style="color: var(--shiki-token-function)"> multilineTextAlignment</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> .center,</span></span>
<span><span style="color: var(--shiki-token-function)"> maxFrameWidth</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> </span><span style="color: var(--shiki-token-constant)">300</span><span style="color: var(--shiki-token-function)">,</span></span>
<span><span style="color: var(--shiki-token-function)"> padding</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> </span><span style="color: var(--shiki-token-constant)">16</span></span>
<span><span style="color: var(--shiki-token-function)"> </span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>