在UITableViewCell中检测UITextView中的数据

时间:2013-12-10 23:37:56

标签: ios uitableview uitextview touchesbegan datadetectortypes

我在UITextView子类中有一个UITableViewCell。它editableuserInteractionEnabled都设置为NO。这允许UITextView检测链接和电话号码等数据。正确检测数据,但由于禁用了userInteraction,因此无法响应该数据的点击。如果我将userInteractionEnabled设置为YES,它可以正常工作,但是由于UITextView吞下了触摸,因此无法选择UITableViewCell

如果用户点击该链接,我想关注该链接,但如果点按基本文字,我希望调用didSelectRowAtIndexPath:

我认为正确的方法是将UITextView子类化并将触摸传递给单元格,但我似乎找不到检测点击是否在链接上的方法。

这是一个类似的问题,但答案只是将所有接触传递给细胞。我想只传递触摸,如果它们不在一块检测到的数据上。 issue enabling dataDetectorTypes on a UITextView in a UITableViewCell

2 个答案:

答案 0 :(得分:7)

1。使用hitTest(_:with:)

UIView有一个名为hitTest(_:with:)的方法,其定义如下:

func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
  

返回包含指定点的视图层次结构(包括其自身)中接收器的最远后代。

UITextViewUIView的子类,您可以在您可能想要创建的UITextView的任何子类中实现此方法。

以下Swift 3示例显示UITableViewController包含单个静态UITableViewCellUITableViewCell嵌入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
    }

}

enter image description here

2。使用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了解有关管理UITextViewUITableViewCell的更多信息。

通过阅读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并收到通知,您仍然可以选择文字并点按以取消选择。