在 UITextview 中获取被点击的单词

Posted

技术标签:

【中文标题】在 UITextview 中获取被点击的单词【英文标题】:Get tapped word in a UITextview 【发布时间】:2018-01-27 09:41:47 【问题描述】:

我添加了一个最初不可编辑的 uitextview。我添加了一个轻击手势,使编辑变为真。在点击手势选择器中,我得到了正在点击的单词。我尝试了很多解决方案,但没有一个适合我作为完整的解决方案。如果不滚动文本视图,每个解决方案都有效。但是,如果我滚动文本视图,则不会检索到确切的单词。这是我获取点击词的代码:

 @objc func handleTap(_ sender: UITapGestureRecognizer) 

    notesTextView.isEditable = true
    notesTextView.textColor = UIColor.white

    if let textView = sender.view as? UITextView 

        var pointOfTap = sender.location(in: textView)
        print("x:\(pointOfTap.x) , y:\(pointOfTap.y)")

        let contentOffsetY = textView.contentOffset.y
        pointOfTap.y += contentOffsetY
        print("x:\(pointOfTap.x) , y:\(pointOfTap.y)")
        word(atPosition: pointOfTap)

 

func word(atPosition: CGPoint) -> String? 
    if let tapPosition = notesTextView.closestPosition(to: atPosition) 
        if let textRange = notesTextView.tokenizer.rangeEnclosingPosition(tapPosition , with: .word, inDirection: 1) 
            let tappedWord = notesTextView.text(in: textRange)
            print("Word: \(tappedWord)" ?? "")
            return tappedWord
        
        return nil
    
    return nil

已编辑:

这是有问题的演示项目。 https://github.com/amrit42087/TextViewDemo

【问题讨论】:

我认为textView.contentOffset.y 应该是textView.scrollView.contentOffset.y 试一试... 不,这不会解决。实际上,当文本在文本之间有空行时,就会出现问题。我的意思是当有一个 nextLine 字符时。 你说不滚动就可以,只知道有空行吗?您可以相应地编辑问题吗? 示例项目会有很大帮助 【参考方案1】:

Swift 4 中最好最简单的方法

方法 1:

第 1 步:在文本视图上添加点按手势

let tap = UITapGestureRecognizer(target: self, action: #selector(tapResponse(recognizer:)))

textViewTC.addGestureRecognizer(tap)

第 2 步:实施点击手势

@objc func tapResponse(recognizer: UITapGestureRecognizer) 
        let location: CGPoint = recognizer.location(in: textViewTC)
        let position: CGPoint = CGPoint(x: location.x, y: location.y)
        let tapPosition: UITextPosition = textViewTC.closestPosition(to: position)!
        guard let textRange: UITextRange = textViewTC.tokenizer.rangeEnclosingPosition(tapPosition, with: UITextGranularity.word, inDirection: 1) else return

        let tappedWord: String = textViewTC.text(in: textRange) ?? ""
        print("tapped word ->", tappedWord)
    

是的,就是这样。去吧。

方法 2:

另一种方法是您可以为 textview 启用链接,然后将其设置为属性。这是一个例子

var foundRange = attributedString.mutableString.range(of: "Terms of Use") //mention the parts of the attributed text you want to tap and get an custom action
attributedString.addAttribute(NSAttributedStringKey.link, value: termsAndConditionsURL, range: foundRange)

将此属性文本设置为Textview和textView.delegate = self

现在你只需要在

中处理响应
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool 

希望对您有所帮助。一切顺利。

【讨论】:

【参考方案2】:

您不需要添加文本视图的内容偏移量。当您将位置转换为滚动视图时,它已经将其内容偏移量考虑在内。

删除:

let contentOffsetY = textView.contentOffset.y
pointOfTap.y += contentOffsetY

应该可以。

【讨论】:

我删除了它。它一直有效,直到文本中没有新行。如果文本中有新行,则在将文本视图滚动到下方一点后,该单词不准确。 我不确定你所说的换行是什么意思。我正在使用的代码在这里:gist.github.com/bgayman/1cd0ade8d14b074f220a6242a088271e 我添加了一个空文本行,它似乎仍然有效,也许你可以看到有什么不同。一般来说,我认为单词代表了相当小的触摸目标,所以如果你没有得到确切的单词,但我会说问题出在界面而不是代码上。 是的,你是对的。我说的是你添加的空行。不同之处在于您正在操场上尝试。在带有文本视图的实际视图控制器中尝试它。滚动文本视图并点击最后一段中的任何单词。这个词不是被点击的那个。 我已经添加了一个有问题的演示项目。你能帮我解决一下吗?【参考方案3】:

请试试这个

//add UITextViewDelegate 

    let termsAndConditionsURL = "someText"
        let privacyURL = "SampleText"

        @IBOutlet weak var terms: UITextView!

     override func viewDidLoad() 
            super.viewDidLoad()

            self.terms.delegate = self

    // Adding Attributed text to TextView

            let str = "By registering, you agree to the Terms and the User Privacy Statement."
            let attributedString = NSMutableAttributedString(string: str)
            var foundRange = attributedString.mutableString.range(of: "Terms")
            attributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue, range: foundRange)
            attributedString.addAttribute(NSAttributedStringKey.underlineStyle , value: NSUnderlineStyle.styleSingle.rawValue, range: foundRange)
            attributedString.addAttribute(NSAttributedStringKey.link, value: termsAndConditionsURL, range: foundRange)
            foundRange = attributedString.mutableString.range(of: "User Privacy Statement")
            attributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue, range: foundRange)
            attributedString.addAttribute(NSAttributedStringKey.underlineStyle , value: NSUnderlineStyle.styleSingle.rawValue, range: foundRange)
            attributedString.addAttribute(NSAttributedStringKey.link, value: privacyURL, range: foundRange)
            terms.attributedText = attributedString
            // Do any additional setup after loading the view.
        


     func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool
        
            if (URL.absoluteString == termsAndConditionsURL)
            
    // Perform Terms Action here
             else if (URL.absoluteString == privacyURL)
            
    // Perform Terms Action here

            
            return false
        

【讨论】:

我的代码已经正确了。如果我将它用于新项目或新控制器,它会很好地工作。但它不适用于现有项目,因为约束可能存在一些问题。我附上了上面的代码。如果您能检查一次并找出问题所在,将会很高兴。【参考方案4】:

Kunal Gupta 的方法 1 适合我。 在两个函数中运行代码只是不起作用,这可能是错误。 我已报告有关此情况的问题。

我 fork 了上面代码的一个 repo:https://github.com/OriTheElf/TextViewDemo

方法返回的两个位置:recognizer.location(in: notesTextView)不一样,可能是notesTextView或者识别器出了什么问题。

【讨论】:

以上是关于在 UITextview 中获取被点击的单词的主要内容,如果未能解决你的问题,请参考以下文章

从 UITextView 中的点击获取单词

如何在 Swift 中制作一个单词可点击的 UITextView?

Ipad dev:在 UITextview 中弹出单词

在 UITextView 的一个单词中长按获取单词

在 UITextView 中的特定单词上添加点击手势识别器

在 UITextView 中定义后取消选择单词