我在UITextView
中嵌入了UITableViewCell
。
该文本视图已禁用滚动,并随着其中的文本而增加高度。
该文本视图具有类似链接的文本部分,该部分具有不同的颜色并带有下划线,并且我在文本视图上附加了点击手势识别器,用于检测用户是否点击了文本的“链接”部分是否为(这是通过使用文本视图的layoutManager
和textContainerInset
来检测点击是否在“链接”内而完成的。这基本上是一种自定义点击测试功能)。
我希望表格视图单元格在用户“缺少”文本视图的链接部分时接收点击并被选中,但无法弄清楚该怎么做。 >
文本视图的userInteractionEnabled
设置为true
。但是,当没有手势识别器连接时,这不会阻止触摸到达表格视图单元。
相反,如果我将其设置为false
,则由于某些原因,即使在文本视图的边界的外部处轻按,单元格选择也会完全停止(但手势识别器仍然有效... 为什么?)。
我尝试覆盖gestureRecognizer(_ :shouldReceive:)
,但是即使返回false
,表视图单元也不会被选中...
我也尝试实现gestureRecognizerShouldBegin(_:)
,但是即使在我执行点击测试并返回false
的情况下,单元也无法获得点击。
如何将错过的水龙头转发回单元格以突出显示?
答案 0 :(得分:1)
使所有视图保持活动状态(即启用用户交互)。
浏览文本视图的手势并禁用不需要的手势。
遍历表视图的gestureRecognisers
数组,并使用requireGestureRecognizerToFail使它们依赖于文本视图的自定义轻击手势。
如果它是静态表视图,则可以在视图加载后执行此操作。对于动态表格视图,请在文本视图单元格的“ willDisplayCell”中执行此操作。
答案 1 :(得分:1)
尝试Swapnil Luktuke's answer(至少据我所知)无济于事,并且尝试了以下所有可能的组合:
UIGestureRecognizerDelegate
的方法,UITapGestureRecognizer
,ignore(_:for:)
等(也许在绝望中我错过了一些明显的东西,但是谁知道...)
...我放弃了并决定遵循@danyapata在我的问题的注释以及 UITextView子类中的建议。
部分基于this Medium post上的代码,我想到了这个UITextView
子类:
import UIKit
/**
Detects taps on subregions of its attributed text that correspond to custom,
named attributes.
- note: If no tap is detected, the behavior is equivalent to a text view with
`isUserInteractionEnabled` set to `false` (i.e., touches "pass through"). The
same behavior doesn't seem to be easily implemented using just stock
`UITextView` and gesture recognizers (hence the need to subclass).
*/
class LinkTextView: UITextView {
private var tapHandlersByName: [String: [(() -> Void)]] = [:]
/**
Adds a custom block to be executed wjhen a tap is detected on a subregion
of the **attributed** text that contains the attribute named accordingly.
*/
public func addTapHandler(_ handler: @escaping(() -> Void), forAttribute attributeName: String) {
var handlers = tapHandlersByName[attributeName] ?? []
handlers.append(handler)
tapHandlersByName[attributeName] = handlers
}
// MARK: - Initialization
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
commonSetup()
}
private func commonSetup() {
self.delaysContentTouches = false
self.isScrollEnabled = false
self.isEditable = false
self.isUserInteractionEnabled = true
}
// MARK: - UIView
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let attributeName = self.attributeName(at: point), let handlers = tapHandlersByName[attributeName], handlers.count > 0 else {
return nil // Ignore touch
}
return self // Claim touch
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
// find attribute name
guard let touch = touches.first, let attributeName = self.attributeName(at: touch.location(in: self)) else {
return
}
// Execute all handlers for that attribute, once:
tapHandlersByName[attributeName]?.forEach({ (handler) in
handler()
})
}
// MARK: - Internal Support
private func attributeName(at point: CGPoint) -> String? {
let location = CGPoint(
x: point.x - self.textContainerInset.left,
y: point.y - self.textContainerInset.top)
let characterIndex = self.layoutManager.characterIndex(
for: location,
in: self.textContainer,
fractionOfDistanceBetweenInsertionPoints: nil)
guard characterIndex < self.textStorage.length else {
return nil
}
let firstAttributeName = tapHandlersByName.allKeys.first { (attributeName) -> Bool in
if self.textStorage.attribute(NSAttributedStringKey(rawValue: attributeName), at: characterIndex, effectiveRange: nil) != nil {
return true
}
return false
}
return firstAttributeName
}
}
通常,我会等几天再接受自己的答案,以防万一出现更好的情况...