Мне удалось решить эту проблему, используя UITextView
вместо UILabel
.Изначально я не хотел использовать UITextView
, потому что мне нужно, чтобы элемент вел себя как UILabel
, а UITextView
может вызвать проблемы с прокруткой, и его предполагаемое использование - это редактируемый текст.Следующий класс, который я написал, заставляет UITextView
вести себя как UILabel
, но с полностью точным обнаружением щелчков и без проблем с прокруткой:
import UIKit
class ClickableLabelTextView: UITextView {
var delegate: DelegateForClickEvent?
var ranges:[(start: Int, end: Int)] = []
var page: String = ""
var paragraph: Int?
var clickedLink: (() -> Void)?
var pressedTime: Int?
var startTime: TimeInterval?
override func awakeFromNib() {
super.awakeFromNib()
self.textContainerInset = UIEdgeInsets.zero
self.textContainer.lineFragmentPadding = 0
self.delaysContentTouches = true
self.isEditable = false
self.isUserInteractionEnabled = true
self.isSelectable = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startTime = Date().timeIntervalSinceReferenceDate
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let clickedLink = clickedLink {
if let startTime = startTime {
self.startTime = nil
if (Date().timeIntervalSinceReferenceDate - startTime <= 0.2) {
clickedLink()
}
}
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
var location = point
location.x -= self.textContainerInset.left
location.y -= self.textContainerInset.top
if location.x > 0 && location.y > 0 {
let index = self.layoutManager.characterIndex(for: location, in: self.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
var count = 0
for range in ranges {
if index >= range.start && index < range.end {
clickedLink = {
self.delegate?.clickedLink(page: self.page, paragraph: self.paragraph, linkNo: count)
}
return self
}
count += 1
}
}
clickedLink = nil
return nil
}
}
Функция hitTest
get вызывается несколько раз, но это никогда не вызываетпроблема, так как clickedLink()
будет вызываться только один раз за клик.Я пытался отключить isUserInteractionEnabled
для разных представлений, но это не помогло и не потребовалось.
Чтобы использовать класс, просто добавьте его в UITextView
.Если вы используете autoLayout
в редакторе Xcode, то отключите Scrolling Enabled
для UITextView
в редакторе, чтобы избежать предупреждений макета.
В файле Swift
, который содержит код дляваш xib
файл (в моем случае это класс для UITableViewCell
, вам нужно установить следующие переменные для вашего интерактивного textView:
ranges
- начальный и конечный индекс каждого интерактивного кликассылка с UITextView
page
- String
для идентификации страницы или представления, содержащего UITextView
paragraph
- Если у вас есть несколько кликабельных UITextView
, назначьте каждому из них номер delegate
- чтобы делегировать события щелчка там, где вы сможете их обработать.
Затем вам нужно создать протокол дляyour delegate
:
protocol DelegateName {
func clickedLink(page: String, paragraph: Int?, linkNo: Int?)
}
Переменные, переданные в clickedLink
, дают вам всю необходимую информацию, чтобы узнать, по какой ссылке щелкнули.