解决NSTextContainer分页时文本截断问题

简介:

解决NSTextContainer分页时文本截断问题

NSTextContainer与NSLayoutManager配合使用可以将大文本文件分页,但是,分页过程中会遇到问题,显示字符被截断的问题:)

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 数据源
    NSString *string = [NSString stringWithContentsOfURL:[NSBundle.mainBundle URLForResource:@"bubizhidaowoshishui" withExtension:@"txt"] usedEncoding:nil
                                                   error:nil];
    
    // 文本容器
    NSTextStorage *storage = [[NSTextStorage alloc] initWithString:string];
    
    // 文本容器的布局管理器
    NSLayoutManager *layoutManager = [NSLayoutManager new];
    [storage addLayoutManager:layoutManager];
    
    // 分段显示文本容器中的内容
    CGSize size = CGSizeMake(300, 540);
    NSTextContainer *textContainer1 = [[NSTextContainer alloc] initWithSize:size];
    [layoutManager addTextContainer:textContainer1];
    
    
    NSTextContainer *textContainer2 = [[NSTextContainer alloc] initWithSize:size];
    [layoutManager addTextContainer:textContainer2];

    
    NSTextContainer *textContainer3 = [[NSTextContainer alloc] initWithSize:size];
    [layoutManager addTextContainer:textContainer3];
    
    
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 20,
                                                                        size.width,
                                                                        size.height)
                                               textContainer:textContainer3];

    textView.layer.borderWidth = 1;
    textView.scrollEnabled     = NO;
    textView.editable          = NO;
    [self.view addSubview:textView];
}

以下是我的运行结果(注意看底下红色的部分,文本被截断了哦):

为什么会被截断呢,按理说,NSLayoutManager会计算好一个size值然后给NSTextContainer让这个NSTextContainer自己适应的.

苹果官方文档里面有描述:

Generating Line Fragment Rectangles

The layout manager lays text within an NSTextContainer object in lines of glyphs. The layout of these lines within the text container is determined by its shape and by any exclusion paths it contains. Wherever the line fragment rectangle intersects a region defined by an exclusion path, the lines in those parts must be shortened or fragmented; if there’s a gap across the entire region, the lines that would overlap it have to be shifted to compensate.

The layout manager proposes a rectangle for a given line and then asks the text container to adjust the rectangle to fit. The proposed rectangle usually spans the text container’s bounding rectangle, but it can be narrower or wider, and it can also lie partially or completely outside the bounding rectangle. The message that the layout manager sends the text container to adjust the proposed rectangle islineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:, which returns the largest rectangle available for the proposed rectangle, based on the direction in which text is laid out. It also returns a rectangle containing any remaining space, such as the space left on the other side of a hole or gap in the text container.

 

The layout manager makes one final adjustment when it actually fits text into the rectangle. This adjustment is a small amount fixed by the text container, called the line fragment padding, which defines the portion on each end of the line fragment rectangle left blank. Text is inset within the line fragment rectangle by this amount (the rectangle itself is unaffected). Padding allows for small-scale adjustment of the text container’s region at the edges (and around any holes) and keeps text from directly abutting any other graphics displayed near the region. You can change the padding from its default value with the lineFragmentPadding property. Note that line fragment padding isn’t a suitable means for expressing margins. For document margins, you should set theUITextView object’s position and size within its enclosing view. And for text margins, you should set thetextContainerInset property of the text view. In addition, you can set indentation values for individual paragraphs using NSMutableParagraphStyle properties such as headIndent.

 

In addition to returning the line fragment rectangle itself, the layout manager returns a rectangle called the used rectangle. This is the portion of the line fragment rectangle that actually contains glyphs or other marks to be drawn. By convention, both rectangles include the line fragment padding and the interline space (which is calculated from the font’s line height metrics and the paragraph’s line spacing parameters). However, the paragraph spacing (before and after) and any space added around the text, such as that caused by center-spaced text, are included only in the line fragment rectangle, and are not included in the used rectangle.

 

 

每个NSTextContainer的frame值都是被NSLayoutManager粗略计算过的,与你设置NSTextContainer的size值略有出入,有时候大些,有时候小些,但误差绝度不会超过一个字符的高度.所以,苹果建议我们在设置UITextView的时候,给这个NSTextContainer预留一定的高度......

解决的方法如下:

效果如下:

这个问题有这么棘手么?其实,我是在黔驴技穷的情况下(github上下载了7-8个相关demo,stackoverflow上搜寻等等途径都无效的情况下)细致研究官方提供的pdf文档才明白过来的:),你懂的.

 

 

附录:

使用自定义字体不是梦:)


- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 数据源
    NSString *string = [NSString stringWithContentsOfURL:[NSBundle.mainBundle URLForResource:@"bubizhidaowoshishui" withExtension:@"txt"] usedEncoding:nil
                                                   error:nil];
    
    // 文本容器
    NSTextStorage *storage = [[NSTextStorage alloc] initWithString:string];
    
    // 文本容器的布局管理器
    NSLayoutManager *layoutManager = [NSLayoutManager new];
    [storage addLayoutManager:layoutManager];
    
    
    // 段落属性
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    paragraphStyle.lineHeightMultiple  = 1.f;                    // 可变行高,乘因数
    paragraphStyle.lineSpacing         = 5.f;                    // 行间距
    paragraphStyle.minimumLineHeight   = 10.f;                   // 最小行高
    paragraphStyle.maximumLineHeight   = 20.f;                   // 最大行高
    paragraphStyle.paragraphSpacing    = 10.f;                   // 段间距
    paragraphStyle.alignment           = NSTextAlignmentLeft;    // 对齐方式
    paragraphStyle.firstLineHeadIndent = 30.f;                   // 段落首文字离边缘间距
    paragraphStyle.headIndent          = 0.f;                    // 段落除了第一行的其他文字离边缘间距
    paragraphStyle.tailIndent          = 0.f;                    // ???????
    [storage addAttribute:NSParagraphStyleAttributeName
                    value:paragraphStyle
                    range:NSMakeRange(0, storage.string.length)];
    
    // 字体属性
    [storage addAttribute:NSFontAttributeName
                    value:[UIFont fontWithName:CUSTOM_FONT(@"新蒂小丸子体", 0) size:15.f]
                    range:NSMakeRange(0, storage.string.length)];
    
    [storage addAttribute:NSForegroundColorAttributeName
                    value:[UIColor redColor]
                    range:NSMakeRange(0, storage.string.length)];
    
    
    // 分段显示文本容器中的内容
    CGSize size = CGSizeMake(300, 520);
    NSTextContainer *textContainer1 = [[NSTextContainer alloc] initWithSize:size];
    [layoutManager addTextContainer:textContainer1];
    
    
    NSTextContainer *textContainer2 = [[NSTextContainer alloc] initWithSize:size];
    [layoutManager addTextContainer:textContainer2];

    
    NSTextContainer *textContainer3 = [[NSTextContainer alloc] initWithSize:size];
    [layoutManager addTextContainer:textContainer3];
    
    
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 20,
                                                                        size.width,
                                                                        size.height + 20)
                                               textContainer:textContainer3];

    textView.layer.borderWidth = 1;
    textView.scrollEnabled     = NO;
    textView.editable          = NO;
    [self.view addSubview:textView];
}

目录
相关文章
要实现文字查询并自动滚动到匹配到的文本范围
要实现文字查询并自动滚动到匹配到的文本范围
|
6月前
|
存储 网络协议 Shell
文本三剑客——awk 截取+过滤+统计(2)
文本三剑客——awk 截取+过滤+统计
|
8月前
|
安全
如何快速断行、分割行、切割行、换行、限制每行字数、平均分割每行字数、序号自动换行、关键字断行等等内容格式整理
该工具用于文本格式处理,能调整每行字数、进行内容断行、提取特定格式内容等。在示例中,展示了如何将一段“交通安全教育”文字按30字每行分隔,并整理出带序号的格式。工具支持序号断行和多级序号设置,适用于笔记整理、文档格式化和内容布局优化,能提升工作效率。下载工具可从百度网盘(提取码:qwu2)或蓝奏云(提取码:2r1z)获取。
|
8月前
解决设置了标签的最大行数,更新标签的内容为富文本内容,导致超过最大行数不显示...问题
解决设置了标签的最大行数,更新标签的内容为富文本内容,导致超过最大行数不显示...问题
38 0
|
JavaScript 前端开发
文本溢出【单行和多行文本溢出】
文本溢出【单行和多行文本溢出】
Word页码调整为“第几页共几页”且去除封面页数的方法
本文介绍在Word文档中,不考虑封面、目录、前言等的页数,为正文添加“第X页,共X页”样式页码的方法~
938 1
Word页码调整为“第几页共几页”且去除封面页数的方法
7-4 统计一行文本的单词个数
7-4 统计一行文本的单词个数
118 0
|
前端开发 JavaScript
优雅的处理文本溢出截断
文本溢出截断是一个比较常见的场景,如新闻列表页、微博列表、商品列表等,溢出截断主要是为了保证界面的整齐。在《前端开发需要知道的 10 个 CSS 技巧》中有提到过多行文本溢出的处理,本文总结一些优雅的处理文本溢出截断的方法。
179 0
优雅的处理文本溢出截断

热门文章

最新文章