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
}
}