当用户在 UITextView 中选择特定单词时

Posted

技术标签:

【中文标题】当用户在 UITextView 中选择特定单词时【英文标题】:when user selects a specific word in UITextView 【发布时间】:2017-10-22 11:08:33 【问题描述】:

我正在实现一个显示完整问题的屏幕

例如:

“一周的第一天是.......”

当用户触摸点时,会出现一个警报,并允许用户输入答案以提交并用他的答案替换点

我的步骤是:

1。我通过以下方式生成了点的范围:

func getRangesOfMissingWords(by missingWord: String = completePlace) -> [(answer: String, range: NSRange)]? 
    let body: NSString = theQuestionBody.text as NSString
    var ranges : [(answer: String, range: NSRange)] = []
    var searchRange = NSRange(location: 0, length: body.length)
    while searchRange.location < body.length 
        searchRange.length = body.length - searchRange.location
        let foundRange = self.getRangeOf(word: missingWord, in: searchRange)
        if foundRange != nil 
            ranges.append((answer: missingWord, range: foundRange!))
            searchRange.location = foundRange!.location + foundRange!.length
        
        else 
            break
        
    
    if ranges.isEmpty == false 
        return ranges
    
    else 
        return nil
    

然后

2.通过以下方式生成围绕缺失单词范围的框架:

func createAnswersFrames(from ranges: [(answer: String, range: NSRange)]) 
    var frames : [WordFrame] = []

    for range in ranges 
        let pos2 = self.theQuestionBody.position(from: self.theQuestionBody.beginningOfDocument, offset: (range.range.location + (range.range.length - 1)))
        let pos1 = self.theQuestionBody.position(from: self.theQuestionBody.beginningOfDocument, offset: range.range.location)
        print("pos1 : \(pos1.debugDescription)")
        print("pos2 : \(pos2.debugDescription)")
        let textRange : UITextRange = self.theQuestionBody.textRange(from: pos1!, to: pos2!)!
        let frame: CGRect = self.theQuestionBody.firstRect(for: textRange)
        frames.append(WordFrame(rect: frame))



    

    self.missingWordsFrames = frames


3.为 UITextView 添加了一个 UITapGestureRecognizer

let sigleTap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(tapRecognizer:)))
theQuestionBody.addGestureRecognizer(sigleTap)

4.and 在观察者中点击

func handleTap(tapRecognizer: UITapGestureRecognizer) 
    guard let ranges = self.missingWordsRanges else 
        return
    

    self.createAnswersFrames(from: ranges)

    let touchPoint = tapRecognizer.location(in: theQuestionBody)

    if self.missingWordsFrames.count > 0 
        for (index,frame) in self.missingWordsFrames.enumerated() 
            frame.isSelected = false
            let validFrame = frame.rect
            if (validFrame.contains(touchPoint)) 
                frame.isSelected = true
                let alertTitle = Helper.getSerroundingText(of: self.missingWordsRanges![index].range, from: self.question.text)
                let alert = UIAlertController(title: alertTitle , message:"", preferredStyle: .alert)
                alert.addTextField  (textField) in
                    textField.placeholder = "Complete..."
                
                alert.addAction(UIAlertAction(title: "OK", style: .default, handler:  [weak alert] (_) in
                    let textField = alert!.textFields![0] 
                    textField.becomeFirstResponder()
                    let answer = textField.text
                    if answer != "" 
                        self.submitAnswer(answer!) 
                    

                ))
                self.present(alert, animated: true, completion: nil)
                break
            

        
    

问题是框架在第一次出现视图时工作正常,但是当用户插入一个字符数很长的答案时,其余的文本将被放置在一个新行中,框架没有'那就不行了。

例子

“x 是…………。y 是…………”

answer -> "SomeLongWordAnswer"

"x 是 SomeLongWordAnswer 。而 y 是

.......”

这里第二个点的框架保持在第一行!

【问题讨论】:

【参考方案1】:

我改变了实现方式,去掉了框架,依赖UITextViewDelegatetextViewDidChangeSelection的委托方法

func textViewDidChangeSelection(_ textView: UITextView) 

        UIMenuController.shared.isMenuVisible = false
        if let missingWordRanges = self.missingWordsRanges 
            for (index,range) in missingWordRanges.enumerated() 
                if range.range == textView.selectedRange 

                    let alertTitle = Helper.getSerroundingText(of: self.missingWordsRanges![index].range, from: self.question.text)
                    let alert = UIAlertController(title: alertTitle , message:"", preferredStyle: .alert)
                    alert.addTextField  (textField) in
                        textField.placeholder = "Complete..."
                    
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler:  [weak alert] (_) in
                        let textField = alert!.textFields![0] // Force unwrapping because we know it exists.
                        textField.becomeFirstResponder()
                        let answer = textField.text
                        if answer != "" 
                            self.submit(answer!, in: index) // cann't press ok if not submitted with an answer so force rapping is okay
                        

                    ))
                    self.present(alert, animated: true, completion: nil)
                    return
                
            
        




然后这里出现了这种方法在选择选择时调用此方法,在iPhone中,通过双击或长点按分接口更改选择。

所以我更新了我的手势

let sigleTap : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(didRecieveGestureOnText(recognizer:)))
theQuestionBody.addGestureRecognizer(sigleTap)

并在基于hris.toanswer的手势处理程序中

func didRecieveGestureOnText(recognizer: UIGestureRecognizer)  
    let textView : UITextView = recognizer.view as! UITextView
    let location = recognizer.location(in: self.theQuestionBody)
    let tapPosition = textView.closestPosition(to: location)
    let textRange = textView.tokenizer.rangeEnclosingPosition(tapPosition!, with: UITextGranularity.word, inDirection: UITextLayoutDirection.right.rawValue)

    self.theQuestionBody.selectedTextRange = textRange
    

所以self.theQuestionBody.selectedTextRange = textRange 这行将触发textViewDidChangeSelection 并且一切都按预期工作

【讨论】:

以上是关于当用户在 UITextView 中选择特定单词时的主要内容,如果未能解决你的问题,请参考以下文章

UITextView 阻止苹果的自动更改

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

当单词以@开头时,从键盘输入更改 UITextView 中的单词格式

UITextView 在数据库中搜索关键字

复制所有文本后隐藏复制和取消选择 UITextView 选项

模仿 UITextView 的默认双击行为