Home > AI > IOS > SwiftUI >

Add clickable link text in SwiftUI

Working Code:

struct ContentView: View {
    @State var text: NSMutableAttributedString = NSMutableAttributedString(string: "")

    var body: some View {
        VStack {
             ClickableTextView(text: self.$text)
        }
        .onAppear{

             let attributedString = NSMutableAttributedString(string: "欢迎使用蛮牛PTE!在您使用蛮牛PTE之前,请您务必认真阅读《用户协议》和《隐私条款》中的各项条款,包括但不限于:为了更好的提供题目练习/内容浏览")
             attributedString.apply(link: "https://www.google.com", subString: "用户协议")

             self.text = attributedString

        }
    }
}


class LinkTextView: UITextView,  UITextViewDelegate {
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        
        UIApplication.shared.open(URL)

        return false
    }
}

struct ClickableTextView: UIViewRepresentable {
    @Binding var text: NSMutableAttributedString

    func makeUIView(context: Context) -> LinkTextView {
        let view = LinkTextView()

        view.dataDetectorTypes = .all
        view.isEditable        = false
        view.isSelectable      = true
        view.delegate          = view
        view.isUserInteractionEnabled = true
        view.linkTextAttributes = [
            .foregroundColor: UIColor.blue,
            .underlineStyle: NSUnderlineStyle.single.rawValue
        ]

        return view
    }

    func updateUIView(_ uiView: LinkTextView, context: Context) {
        uiView.attributedText = text
    }
}


extension NSMutableAttributedString {

    func apply(link: String, subString: String)  {
        let ranges = self.string.ranges(of: subString)
        if ranges.count != 0 {
            for r in ranges {
                self.addAttributes([NSAttributedString.Key.link: link], range: NSRange(r, in: self.string))
            }
        }
    }
}


extension String {
    func ranges(of substring: String, options: CompareOptions = [], locale: Locale? = nil) -> [Range<Index>] {
        var ranges: [Range<Index>] = []
        while ranges.last.map({ $0.upperBound < self.endIndex }) ?? true,
            let range = self.range(of: substring, options: options, range: (ranges.last?.upperBound ?? self.startIndex)..<self.endIndex, locale: locale)
        {
            ranges.append(range)
        }
        return ranges
    }
}

Leave a Reply