5

I am trying to custom change the default font color of hyperlink in a given markdown string with SwiftUI. Something equivalent to txtString.linkTextAttributes = [ .foregroundColor: UIColor.red ] of UIKit. Here's my code:


import SwiftUI

struct TextViewAttributedString: View {
    var markdownString: String
    var body: some View {
        Text(convertIntoAttributedString(markdownString:markdownString))
    }
    
    private func convertIntoAttributedString(markdownString: String) -> AttributedString {
        guard var attributedString = try? AttributedString(
            markdown: markdownString,
            options: AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true,
                                                             interpretedSyntax: .inlineOnlyPreservingWhitespace))
        else {
            return AttributedString(markdownString)
        }
        attributedString.font = .custom("Times New Roman", size: 16, relativeTo: .body)
        
        let runs = attributedString.runs
        for run in runs {
            let range = run.range
            if let textStyle = run .inlinePresentationIntent {
                if textStyle.contains(.stronglyEmphasized) { // .stronglyEmphasized is available
                    // change foreground color of bold text
                    attributedString[range].foregroundColor = .green
                }
                if textStyle.contains(.linkTextAttributes) { // compiler error since .linkTextAttributes not available
                    // change color here but .linkTextAttributes is not available in inlinePresentationIntent
                    // Any other way to change the hyperlink color?
                }
            }
        }
        return attributedString
    }
}

Example View where AttribtedString being used

import SwiftUI

struct AttributedStringView: View {
    let text: String = "**Bold** regular and _italic_ \nnewline\n[hyperlink](www.google.com)"
    var body: some View {
        TextViewAttributedString(markdownString: text)
    }
}

struct AttributedStringView_Previews: PreviewProvider {
    static var previews: some View {
        AttributedStringView()
    }
}

Result: Result Screen

Reference Docs: https://developer.apple.com/documentation/foundation/attributedstring https://developer.apple.com/videos/play/wwdc2021/10109/

Vijay Lama
  • 96
  • 1
  • 6
  • 1
    have a look at this SO post/answer: https://stackoverflow.com/questions/70214588/change-text-link-color-swiftui – workingdog support Ukraine Mar 12 '22 at 00:55
  • 2
    Yeah, I looked into the post before but there the solution is to simply change the accentcolor of the whole string. My case requires applying color to the markdown link only that is a part of the string. Ty – Vijay Lama Mar 12 '22 at 01:06

4 Answers4

5

The easiest solution for me was:

 Text(.init("some text **[google.com](https://google.com)**"))
     .accentColor(.red)
                                      
Timmy
  • 4,098
  • 2
  • 14
  • 34
vigdora
  • 319
  • 4
  • 11
4
        if run.link != nil {
            // change foreground color of link
            attributedString[range].foregroundColor = .orange
        }
ChrisR
  • 9,523
  • 1
  • 8
  • 26
  • For some reason, this is not working for me. Links get recognized in runs, but changing the color does nothing. I am able to change background color, or the font - but foreground color and underline style stays the same :( – igr Oct 19 '22 at 10:09
  • @igr in my case link doesnt even get recognized in runs... – vigdora Feb 05 '23 at 08:27
  • 1
    @igr it doesnt work since link is not recognized inside inlinePresentationIntent. one should use = > if let textStyle = run .link { someCode} – vigdora Feb 05 '23 at 10:57
2

I had to replace the AttributeContainer.

if run.link != nil {
    var container = AttributeContainer()
    container.foregroundColor = .red
    attributedString[range].setAttributes(container)
}
igr
  • 10,199
  • 13
  • 65
  • 111
0

iOS 15: You can initialize SwiftUIs Text with an AttributedString. You can create the AttributedString from a markdown string, find the link labels with regex and change the color for that part of the AttributedString.

let markdown = "**Bold** regular and _italic_ [hyperlink cool](www.google.com)"
let linkColor = UIColor.green

var body: some View {
    Text(makeAttributedString())
}

func makeAttributedString() -> AttributedString {
    var string = (try? AttributedString(markdown: markdown)) ?? AttributedString("Error markdown")
    let linkLabels = getLinkLabels(markdownString: markdown)
    for label in linkLabels {
        if let range = string.range(of: label) {
            string[range].foregroundColor = linkColor
        }
    }
    return string
}

func getLinkLabels(markdownString: String) -> [String] {
    guard let regex = try? NSRegularExpression(pattern: "\\[[a-zA-Z0-9_ ]*\\]") else { return [] }
    let results = regex.matches(in: markdownString,
                                range: NSRange(markdownString.startIndex..., in: markdownString))
    let labels = results.compactMap {
        Range($0.range, in: markdownString).map { String(markdownString[$0]) }
    }
    // removing the brackets from the link labels before return
    return labels.map { $0.trimmingCharacters(in: CharacterSet(charactersIn: "[]")) }
}
mw_906
  • 208
  • 2
  • 5
  • Thank you so much. This solves my problem and very thankful but I accepted ChrisR's answer because his answer solves more native way using AttributedString. Thank you again. – Vijay Lama Mar 12 '22 at 16:13