我在UITextView
子类中有一个UITableViewCell
。它editable
和userInteractionEnabled
都设置为NO
。这允许UITextView
检测链接和电话号码等数据。正确检测数据,但由于禁用了userInteraction,因此无法响应该数据的点击。如果我将userInteractionEnabled
设置为YES
,它可以正常工作,但是由于UITextView吞下了触摸,因此无法选择UITableViewCell
。
如果用户点击该链接,我想关注该链接,但如果点按基本文字,我希望调用didSelectRowAtIndexPath:
。
我认为正确的方法是将UITextView
子类化并将触摸传递给单元格,但我似乎找不到检测点击是否在链接上的方法。
这是一个类似的问题,但答案只是将所有接触传递给细胞。我想只传递触摸,如果它们不在一块检测到的数据上。 issue enabling dataDetectorTypes on a UITextView in a UITableViewCell
答案 0 :(得分:7)
hitTest(_:with:)
UIView
有一个名为hitTest(_:with:)
的方法,其定义如下:
func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
返回包含指定点的视图层次结构(包括其自身)中接收器的最远后代。
UITextView
是UIView
的子类,您可以在您可能想要创建的UITextView
的任何子类中实现此方法。
以下Swift 3示例显示UITableViewController
包含单个静态UITableViewCell
。 UITableViewCell
嵌入UITextView
。点击UITextView
内的链接即可启动Safari应用;点击UITextView
内的基本文字会触发推送到以下UIViewController
。
LinkTextView.swift
import UIKit
class LinkTextView: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
isScrollEnabled = false
isEditable = false
isUserInteractionEnabled = true
isSelectable = true
dataDetectorTypes = .link
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Get the character index from the tap location
let characterIndex = layoutManager.characterIndex(for: point, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
// if we detect a link, handle the tap by returning self...
if let _ = textStorage.attribute(NSLinkAttributeName, at: characterIndex, effectiveRange: nil) {
return self
}
// ... otherwise return nil ; the tap will go on to the next receiver
return nil
}
}
TextViewCell.swift
import UIKit
class TextViewCell: UITableViewCell {
@IBOutlet weak var textView: LinkTextView!
}
TableViewController.swift
import UIKit
class TableViewController: UITableViewController {
@IBOutlet weak var cell: TextViewCell!
override func viewDidLoad() {
super.viewDidLoad()
// Add text to cell's textView
let text = "http://www.google.com Lorem ipsum dolor sit amet, consectetur adipiscing elit, http://www.yahoo.com sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
cell.textView.text = text
}
}
point(inside:with:)
作为覆盖hitTest(_:with:)
的替代方法,您可以使用point(inside:with:)
。 point(inside:with:)
有以下声明:
func point(inside point: CGPoint, with event: UIEvent?) -> Bool
返回一个布尔值,指示接收器是否包含指定的点。
以下代码显示了如何在point(inside:with:)
子类中实现hitTest(_:with:)
而不是UITextView
:
LinkTextView.swift
import UIKit
class LinkTextView: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
isScrollEnabled = false
isEditable = false
isUserInteractionEnabled = true
isSelectable = true
dataDetectorTypes = .link
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// Get the character index from the tap location
let characterIndex = layoutManager.characterIndex(for: point, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
// if we detect a link, handle the tap by returning true...
if let _ = textStorage.attribute(NSLinkAttributeName, at: characterIndex, effectiveRange: nil) {
return true
}
// ... otherwise return false ; the tap will go on to the next receiver
return false
}
}
此GitHub仓库中提供了完整的项目:LinkTextViewCell。
您可以阅读Swifty Approach to Handling UITextViews With Links in Cells了解有关管理UITextView
内UITableViewCell
的更多信息。
通过阅读Apple的Guide and Sample Code: "Event Delivery: The Responder Chain",您可以详细了解hitTest(_:with:)
和point(inside:with:)
之间的区别。
答案 1 :(得分:3)
我有同样的问题。我通过继承UITextView并添加协议来解决它:
@protocol SOTextViewDelegate;
@interface SOTextView : UITextView
@property (nonatomic, weak) id<SOTextViewDelegate> soDelegate;
@end
@protocol SOTextViewDelegate <NSObject>
@optional
- (void)soTextViewWasTapped:(SOTextView *)soTextview;
@end
在实施中,我刚刚添加了这个:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.selectedRange.length == 0 &&
[_soDelegate respondsToSelector:@selector(soTextViewWasTapped:)]))
[_soDelegate soTextViewWasTapped:self];
}
此委托将告诉自定义单元格轻触textView。我的自定义单元格也有一个委托,以触发其实际选择。
现在您可以点按一个链接,它会打开,您可以点按textView并收到通知,您仍然可以选择文字并点按以取消选择。