如何在不调用 textViewDidChangeSelection 的情况下设置 NSAttributedString 属性?

Posted

技术标签:

【中文标题】如何在不调用 textViewDidChangeSelection 的情况下设置 NSAttributedString 属性?【英文标题】:How to set NSAttributedString attributes without invoking textViewDidChangeSelection? 【发布时间】:2017-08-17 19:08:43 【问题描述】:

我想要做什么:

UITextView 中有一个属性文本,当用户点击文本的某处(不选择文本,只是将闪烁的光标放在其他地方)时,光标周围的整个句子会改变颜色。

我是如何尝试这样做的:

   private func setFocus(boundarySymbols: CharacterSet) 
    let mutableString = NSMutableAttributedString(attributedString: noteTextView.attributedText)

    mutableString.beginEditing()

    unfocusWholeText(mutableString: mutableString)
    let selectedRange = noteTextView.selectedRange
    guard let caretPosition = findCaretPosition() else  return 
    guard let focusRange = findRangeToFocus(in: noteTextView.text, around: caretPosition, of: boundarySymbols) else  return 
    text.addAttribute(NSForegroundColorAttributeName, value: ThemeManager.theme.note.text, range: focusRange)

    mutableString.endEditing()

    noteTextView.attributedText = mutableString
    noteTextView.selectedRange = selectedRange

当我在UITextViewDelegateshouldChangeTextIn 方法中调用此函数时效果很好,但它仅在用户键入时才有效,我也想在用户仅更改选择时调用此函数,因此textViewDidChangeSelection 似乎适合完美。

但是有一个问题:

当我将修改后的属性字符串分配给 UITextView 时,它会将光标移动到文本的末尾(通过保存光标位置并在所有更改后再次设置它来修复它)。但问题是这两个东西都在移动光标,并再次调用textViewDidChangeSelection,因此发生无限循环(textViewDidChangeSelection 正在调用我的setFocus,它正在修改光标位置,所以再次调用textViewDidChangeSelection)。

所以问题是:

我可以在不创建 NSMutableAttributedString 并将其重新分配给 UITextView.attributedText 以省略将光标移动到文本末尾并返回的情况下更改属性字符串的属性(基本上只是颜色)吗?

或者也许有一种方法可以在不应该移动光标时以某种方式关闭它,或者让它暂时不调用委托?

【问题讨论】:

【参考方案1】:

经过长时间的战斗,我最终找到了一个之前没有想到的简单解决方法。

setFocus 放置在textViewDidChangeSelection 函数中,我将UITextViewDelegate 设置为nil,在更改颜色和设置光标后,我再次分配委托。不是一个干净的解决方案,但至少可以工作。

【讨论】:

【参考方案2】:

我为我的一个项目写了一个UITextField 扩展,让你可以这样做:

extension UITextField 

    enum ShouldChangeCursor 
        case incrementCursor
        case preserveCursor
    

    func preserveCursorPosition(withChanges mutatingFunction: (UITextPosition?) -> (ShouldChangeCursor)) 

        //save the cursor positon
        var cursorPosition: UITextPosition? = nil
        if let selectedRange = self.selectedTextRange 
            let offset = self.offset(from: self.beginningOfDocument, to: selectedRange.start)
            cursorPosition = self.position(from: self.beginningOfDocument, offset: offset)
        

        //make mutaing changes that may reset the cursor position
        let shouldChangeCursor = mutatingFunction(cursorPosition)

        //restore the cursor
        if var cursorPosition = cursorPosition 

            if shouldChangeCursor == .incrementCursor 
                cursorPosition = self.position(from: cursorPosition, offset: 1) ?? cursorPosition
            

            if let range = self.textRange(from: cursorPosition, to: cursorPosition) 
                self.selectedTextRange = range
            
        

    


您可以在不改变光标位置的情况下使用它来更新属性文本:

textField.preserveCursorPosition(withChanges:  _ in
    textField.attributedText = ...
    return .preserveCursor
)

【讨论】:

您正在保存光标位置并稍后再次设置它,就像我一样,它无论如何都会调用textViewDidChangeSelectiondelegate 所以它对我没有帮助

以上是关于如何在不调用 textViewDidChangeSelection 的情况下设置 NSAttributedString 属性?的主要内容,如果未能解决你的问题,请参考以下文章

选择预测文本并将其添加到文本时,不会触发 iOS textViewDidChange

在 UITextView 委托方法 textViewDidChange 中将文本保存在 Core Data 中是不是有效?

UITextView 保存数据

如何在不调用 fixture.detectChanges() 的情况下绑定模板?

如何在不使用表单的情况下从 jsp 调用 servlet

如何在不等待完成的情况下调用存储过程?