UITextView插入NSAttributedString后光标跳至末尾 列表编辑异常求助
问题描述
我正在基于UITextView实现支持有序列表(ol)/无序列表(ul)的富文本编辑器,当前运行情况如下:
正常光标运行效果:
编辑器首先会将HTML内容转换为NSAttributedString,使用如下HTML内容时运行正常:
<ol> <li>123456</li> <li>123456</li> <li>123456</li> <li>123456</li> <li>123456</li> </ol> <br/> <ul> <li>123456</li> <li>123456</li> <li>123456</li> </ul> <br/> <ol> <li>abcdef</li> <li>abcdef</li> <li>abcdef</li> </ol>
但如果在内容顶部添加普通文本,就会出现异常行为:
光标跳转异常效果:
可以看到,新建有序列表行后输入字符时,光标会直接跳转到内容末尾,看起来系统在尝试重置我之前创建的行的样式。
出现异常时转换所用的HTML内容如下:
Just some text on top <ol> <li>123456</li> <li>123456</li> <li>123456</li> <li>123456</li> <li>123456</li> </ol> <br/> <ul> <li>123456</li> <li>123456</li> <li>123456</li> </ul> <br/> <ol> <li>abcdef</li> <li>abcdef</li> <li>abcdef</li> </ol>
我发现每次新建有序列表行输入字符时,系统都会重新格式化当前有序列表的样式,导致控件的attributedText发生变化,但我不清楚该问题的来源。
原因分析
该问题是UITextView内置HTML富文本解析的固有特性:系统会将<ol>/<ul>标签转换为自带NSListStyleAttribute的段落属性,当富文本的起始位置为非列表的普通文本时,编辑列表项触发的样式重计算逻辑会触发系统全量重写整个attributedText,原有光标位置失效,最终自动跳转到文本末尾。
解决方案
- 方案1:调整HTML结构
转NSAttributedString前,给所有无标签包裹的顶层普通文本统一套上<p>标签,保证顶层内容都是独立块级元素,避免系统解析时把普通文本和后续列表的段落样式合并。解析时固定解析参数,减少不可控性:NSDictionary *options = @{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding) }; NSAttributedString *attrStr = [[NSAttributedString alloc] initWithData:[html dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:nil error:nil]; - 方案2:手动恢复光标位置
实现UITextView代理方法,在系统修改文本前记录当前光标偏移量,文本变更完成后手动重置光标位置:- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { NSInteger targetOffset = range.location + text.length; dispatch_async(dispatch_get_main_queue(), ^{ textView.selectedRange = NSMakeRange(targetOffset, 0); }); return YES; } - 方案3:自定义列表样式
自行实现列表序号/圆点绘制、行首缩进逻辑,不在NSAttributedString中使用系统自带的NSListStyleAttribute属性,从根源避免系统触发列表样式重计算。 - 方案4:统一添加前置占位元素
不想修改现有解析逻辑的前提下,可以直接在所有HTML内容开头加一个高度为0的空<p>标签,让列表的父级结构和无顶部普通文本的场景保持一致,即可规避该问题。
内容的提问来源于stack exchange,提问作者Huy.Vu
相关产品推荐
相关产品推荐

