用富文本替换UITextView的文本

17

我有一个UITextView,当用户输入文本时,我想即时对文本进行格式化。就像语法高亮一样...

为此,我想使用UITextView ...

除了一个问题外,一切都运行良好:我从文本视图中获取文本,并从中创建一个NSAttributedString。我对此属性字符串进行了一些编辑,并将其设置回textView.attributedText

这发生在用户每次输入时。因此,我必须在编辑attributedText之前记住所选的selectedTextRange,并在之后将其设置回去,以便用户可以在之前输入的位置继续输入。唯一的问题是,一旦文本足够长需要滚动,如果我打字速度较慢,UITextView现在会开始向顶部滚动。

下面是一些示例代码:

- (void)formatTextInTextView:(UITextView *)textView
{
  NSRange selectedRange = textView.selectedRange;
  NSString *text = textView.text;

  // This will give me an attributedString with the base text-style
  NSMutableAttributedString *attributedString = [self attributedStringFromString:text];

  NSError *error = nil;
  NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"#(\\w+)" options:0 error:&error];
  NSArray *matches = [regex matchesInString:text
                                    options:0
                                      range:NSMakeRange(0, text.length)];

  for (NSTextCheckingResult *match in matches)
  {
    NSRange matchRange = [match rangeAtIndex:0];
    [attributedString addAttribute:NSForegroundColorAttributeName
                             value:[UIColor redColor]
                             range:matchRange];
  }

  textView.attributedText = attributedString;
  textView.selectedRange = selectedRange;
}

有没有不直接使用CoreText的解决方案?我喜欢UITextView选择文本等功能....


1
@NANNAV 感谢您通过编辑帖子来改进SO。然而,真的没有理由去任意加粗强调您似乎在不同的 帖子中 引入 这种 任意 加粗。这只会引入噪音,并且实际上可能被认为是有害的。 - Xavi López
4个回答

40

我不确定这是否是正确的解决方案,但它可以工作。
只需在格式化文本之前禁用滚动,然后在格式化后启用即可

- (void)formatTextInTextView:(UITextView *)textView
{
    textView.scrollEnabled = NO;
    NSRange selectedRange = textView.selectedRange;
    NSString *text = textView.text;

    // This will give me an attributedString with the base text-style
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];

    NSError *error = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"#(\\w+)" options:0 error:&error];
    NSArray *matches = [regex matchesInString:text
                                      options:0
                                        range:NSMakeRange(0, text.length)];

    for (NSTextCheckingResult *match in matches)
    {
        NSRange matchRange = [match rangeAtIndex:0];
        [attributedString addAttribute:NSForegroundColorAttributeName
                                 value:[UIColor redColor]
                                 range:matchRange];
    }

    textView.attributedText = attributedString;
    textView.selectedRange = selectedRange;
    textView.scrollEnabled = YES;
}

谢谢你的回答!!!与此同时,我找到了另一个解决方案... 我只是子类化了UITextView并重写了'scrollRectToVisible:animated:',让它调用没有动画的super... 你的解决方案似乎更加流畅 :)再次感谢! - Georg
Swift 5+更新:NSForegroundColorAttributeName应该替换为NSAttributedString.Key.foregroundColor - Sal

4

我使用Sergey的答案并将其移植到了Swift 2:

func formatTextInTextView(textView: UITextView) {
    textView.scrollEnabled = false
    let selectedRange = textView.selectedRange
    let text = textView.text

    // This will give me an attributedString with the base text-style
    let attributedString = NSMutableAttributedString(string: text)

    let regex = try? NSRegularExpression(pattern: "#(\\w+)", options: [])
    let matches = regex!.matchesInString(text, options: [], range: NSMakeRange(0, text.characters.count))

    for match in matches {
        let matchRange = match.rangeAtIndex(0)
        attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: matchRange)
    }

    textView.attributedText = attributedString
    textView.selectedRange = selectedRange
    textView.scrollEnabled = true
}

什么时候我调用这个方法?实际上我想要和你的代码一样,但是作为链接。你能分享那段代码吗? - Rajni Johar Raswant

1

Swift 2.0:

let myDisplayTxt:String = "Test String"

    let string: NSMutableAttributedString = NSMutableAttributedString(string: self.myDisplayTxt)
    string.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, 5))
    string.addAttribute(String(kCTForegroundColorAttributeName), value: UIColor.redColor().CGColor as AnyObject, range: NSMakeRange(0, 5))

    self.sampleTextView.attributedText =  string

1
在 Swift 4 中:
func createAttributedText() {
    let stringText = "Test String"
    let stringCount = stringText.count
    let string: NSMutableAttributedString = NSMutableAttributedString(string: stringText)

    string.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSMakeRange(0, stringCount))

    self.textView.attributedText =  string
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接