forked from gonzalezreal/AttributedText
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAttributedTextImpl+iOS.swift
87 lines (72 loc) · 2.66 KB
/
AttributedTextImpl+iOS.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#if os(iOS)
import SwiftUI
extension AttributedTextImpl: UIViewRepresentable {
func makeUIView(context: Context) -> TextView {
TextView()
}
func updateUIView(_ uiView: TextView, context: Context) {
uiView.attributedText = attributedText
uiView.maxLayoutWidth = maxLayoutWidth
uiView.textContainer.maximumNumberOfLines = context.environment.lineLimit ?? 0
uiView.textContainer.lineBreakMode = NSLineBreakMode(
truncationMode: context.environment.truncationMode
)
uiView.openLink = onOpenLink ?? { context.environment.openURL($0) }
textSizeViewModel.didUpdateTextView(uiView)
}
}
extension AttributedTextImpl {
final class TextView: UITextView {
var maxLayoutWidth: CGFloat = 0 {
didSet {
guard maxLayoutWidth != oldValue else { return }
invalidateIntrinsicContentSize()
}
}
var openLink: ((URL) -> Void)?
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
self.backgroundColor = .clear
self.textContainerInset = .zero
self.isEditable = false
self.isSelectable = false
self.isScrollEnabled = false
self.textContainer.lineFragmentPadding = 0
self.addGestureRecognizer(
UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
guard maxLayoutWidth > 0 else {
return super.intrinsicContentSize
}
return sizeThatFits(CGSize(width: maxLayoutWidth, height: .greatestFiniteMagnitude))
}
@objc func handleTap(sender: UITapGestureRecognizer) {
guard let url = self.url(at: sender.location(in: self)) else {
return
}
openLink?(url)
}
private func url(at location: CGPoint) -> URL? {
guard let attributedText = self.attributedText else { return nil }
let index = indexOfCharacter(at: location)
return attributedText.attribute(.link, at: index, effectiveRange: nil) as? URL
}
private func indexOfCharacter(at location: CGPoint) -> Int {
let locationInTextContainer = CGPoint(
x: location.x - self.textContainerInset.left,
y: location.y - self.textContainerInset.top
)
return self.layoutManager.characterIndex(
for: locationInTextContainer,
in: self.textContainer,
fractionOfDistanceBetweenInsertionPoints: nil
)
}
}
}
#endif