metatext/Views/TouchFallthroughTextView.swift

101 lines
3.3 KiB
Swift
Raw Normal View History

2020-08-21 02:29:01 +00:00
// Copyright © 2020 Metabolist. All rights reserved.
import UIKit
2020-09-08 02:35:28 +00:00
final class TouchFallthroughTextView: UITextView {
2020-08-21 02:29:01 +00:00
var shouldFallthrough: Bool = true
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
initializationActions()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initializationActions()
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
shouldFallthrough ? urlAndRect(at: point) != nil : super.point(inside: point, with: event)
}
override var selectedTextRange: UITextRange? {
get { shouldFallthrough ? nil : super.selectedTextRange }
set {
if !shouldFallthrough {
super.selectedTextRange = newValue
}
}
}
override var intrinsicContentSize: CGSize {
return text == "" ? .zero : super.intrinsicContentSize
}
func urlAndRect(at point: CGPoint) -> (URL, CGRect)? {
guard
let pos = closestPosition(to: point),
let range = tokenizer.rangeEnclosingPosition(
pos, with: .character,
inDirection: UITextDirection.layout(.left))
else { return nil }
let urlAtPointIndex = offset(from: beginningOfDocument, to: range.start)
guard let url = attributedText.attribute(
.link, at: offset(from: beginningOfDocument, to: range.start),
effectiveRange: nil) as? URL
else { return nil }
let maxLength = attributedText.length
var min = urlAtPointIndex
var max = urlAtPointIndex
attributedText.enumerateAttribute(
.link,
in: NSRange(location: 0, length: urlAtPointIndex),
options: .reverse) { attribute, range, stop in
if let attributeURL = attribute as? URL, attributeURL == url, min > 0 {
min = range.location
} else {
stop.pointee = true
}
}
attributedText.enumerateAttribute(
.link,
in: NSRange(location: urlAtPointIndex, length: maxLength - urlAtPointIndex),
options: []) { attribute, range, stop in
if let attributeURL = attribute as? URL, attributeURL == url, max < maxLength {
max = range.location + range.length
} else {
stop.pointee = true
}
}
var urlRect = CGRect.zero
layoutManager.enumerateEnclosingRects(
forGlyphRange: NSRange(location: min, length: max - min),
withinSelectedGlyphRange: NSRange(location: NSNotFound, length: 0),
in: textContainer) { rect, _ in
if urlRect.origin == .zero {
urlRect.origin = rect.origin
}
urlRect = urlRect.union(rect)
}
return (url, urlRect)
}
}
private extension TouchFallthroughTextView {
private func initializationActions() {
textDragInteraction?.isEnabled = false
textContainerInset = .zero
textContainer.lineFragmentPadding = 0
linkTextAttributes = [.foregroundColor: tintColor as Any, .underlineColor: UIColor.clear]
}
}