如何删除NSAttributedString的整个块

时间:2015-05-20 21:15:06

标签: ios uitextview nsattributedstring

我正在开发一款应用,可让用户输入@ 用户名来提及其他用户。虽然我有found a way来检测@字符并在文本字符串中突出显示所选用户名,但我不确定如何在用户提及用户但又希望从提及中删除该用户时管理该事件。 / p>

例如,在Facebook应用中,当您提及某人时,UITextView中该人物的名称会以浅蓝色背景突出显示。但是,当您开始删除该提及时,当您删除属性字符串中的最后一个字符时,整个提及将被删除。

因此,我正在寻找一种方法,当用户删除属性字符串的最后一个字符时,我可以捕获该方法,以便删除整个属性字符串并从文本视图中完全删除提及。

3 个答案:

答案 0 :(得分:3)

为了达到上述所需的行为,我最终做了以下工作。我实现了所有这些方法作为放置我的UITextView的视图控制器的一部分,所以我将 UITextViewDelegate 协议添加到我的视图控制器。然后,我实现了以下方法:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    //if this is true, then the user just deleted a character by using backspace
    if(range.length == 1 && text.length == 0){            
        NSUInteger cursorPosition = range.location; //gets cursor current position in the text
        NSRange attrRange; //will store the range of the text that holds specific attributes
        NSDictionary *attrs = [_content.attributedText attributesAtIndex:cursorPosition effectiveRange:&attrRange];

        //check if the attributes of the attributed text in the cursor's current position correspond to what you want to delete as a block
        if([attrs objectForKey:NSBackgroundColorAttributeName]){
            NSAttributedString *newStr = [_content.attributedText attributedSubstringFromRange:NSMakeRange(0, attrRange.location)]; //creates a new NSAttributed string without the block of text you wanted to delete
            _content.attributedText = newStr; //substitute the attributed text of your UITextView
            return NO;
        }
    }
    return YES;
}

答案 1 :(得分:1)

如果您已经将整个@username包围了属性,子类UITextView并覆盖-deleteBackward以检查游标是否位于其中一个用户名属性中。如果没有,只需调用超级实现,否则找到起始位置和属性用户名的长度并删除整个范围。

答案 2 :(得分:1)

路易斯·德尔加多的出色回答给了我一个很好的跳跃点,但也有一些缺点:

  • 始终清除字符串
  • 的结尾
  • 不会处理插入令牌
  • 不会处理修改包含令牌的选定文本

这是我的版本(Swift 4):

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    if range.location < textView.attributedText.length {

        var shouldReplace = false
        var tokenAttrRange = NSRange()
        var currentReplacementRange = range
        let tokenAttr = NSAttributedStringKey.foregroundColor

        if range.length == 0 {
            if nil != textView.attributedText.attribute(tokenAttr, at: range.location, effectiveRange: &tokenAttrRange) {
                currentReplacementRange = NSUnionRange(currentReplacementRange, tokenAttrRange)
                shouldReplace = true
            }
        } else {
            // search the range for any instances of the desired text attribute
            textView.attributedText.enumerateAttribute(tokenAttr, in: range, options: .longestEffectiveRangeNotRequired, using: { (value, attrRange, stop) in
                // get the attribute's full range and merge it with the original
                if nil != textView.attributedText.attribute(tokenAttr, at: attrRange.location, effectiveRange: &tokenAttrRange) {
                    currentReplacementRange = NSUnionRange(currentReplacementRange, tokenAttrRange)
                    shouldReplace = true
                }
            })
        }

        if shouldReplace {
            // remove the token attr, and then replace the characters with the input str (which can be empty on a backspace)
            let mutableAttributedText = textView.attributedText.mutableCopy() as! NSMutableAttributedString
            mutableAttributedText.removeAttribute(tokenAttr, range: currentReplacementRange)
            mutableAttributedText.replaceCharacters(in: currentReplacementRange, with: text)
            textView.attributedText = mutableAttributedText

            // set the cursor position to the end of the edited location
            if let cursorPosition = textView.position(from: textView.beginningOfDocument, offset: currentReplacementRange.location + text.lengthOfBytes(using: .utf8)) {
                textView.selectedTextRange = textView.textRange(from: cursorPosition, to: cursorPosition)
            }

            return false
        }
    }

    return true
}

如果需要覆盖超类,请添加override关键字,而不是在结尾返回true,返回

super.textView(textView, shouldChangeTextIn: range, replacementText: text)

您可以将tokenAttr更改为任何标准或自定义文字属性。如果将enumerateAttribute更改为enumerateAttributes,您甚至可以查找文本属性的组合。