gpt4 book ai didi

ios - 带有手势识别器的 UITextView - 有条件地将触摸转发到父 View

转载 作者:行者123 更新时间:2023-11-28 20:55:44 26 4
gpt4 key购买 nike

我在 UITableViewCell 中嵌入了一个 UITextView

TextView 已禁用滚动,并随着其中的文本增加高度。

TextView 有一个类似链接的文本部分,它带有不同的颜色和下划线,我有一个点击手势识别器附加到 TextView ,检测用户是否点击文本的“链接”部分 (这是使用 TextView 的 layoutManagertextContainerInset 来检测点击是否落在“链接”或不是。它基本上是一个自定义 HitTest 函数)。


我希望表格 View 单元格接收点击并在用户“错过” TextView 的链接部分时被选中,但不知道该怎么做。 p>


TextView 将 userInteractionEnabled 设置为 true。但是,当没有附加手势识别器时,这不会阻止触摸到达表格 View 单元格。

相反,如果我将其设置为 false,由于某种原因,单元格选择将完全停止,即使在 TextView 边界的外部处点击也是如此(但手势识别器 仍然有效... 为什么?)。


我尝试过的

我已经尝试覆盖 gestureRecognizer(_ :shouldReceive:),但即使我返回 false,表格 View 单元格也不会被选中...

我也尝试过实现 gestureRecognizerShouldBegin(_:),但是即使我执行 HitTest 并返回 false,单元格也不会得到点击.


如何将错过的点击转发回单元格,以突出显示它?

最佳答案

尝试后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
}
}

像往常一样,我会等几天再接受我自己的答案,以防万一出现更好的东西......

关于ios - 带有手势识别器的 UITextView - 有条件地将触摸转发到父 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52553213/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com