解决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];
}

目录
相关文章
|
4月前
|
人工智能 弹性计算 前端开发
如何实现单行/多行文本溢出的省略样式?
如何实现单行/多行文本溢出的省略样式?
|
1月前
文本格式化标记
文本格式化标记。
18 2
|
3月前
|
前端开发 JavaScript UED
|
3月前
|
前端开发
css实现单行文本溢出以及多行文本溢出
css实现单行文本溢出以及多行文本溢出
|
8月前
|
JavaScript 前端开发
文本溢出【单行和多行文本溢出】
文本溢出【单行和多行文本溢出】
文本超出显示省略号--截取字段
文本超出显示省略号--截取字段
|
10月前
多行文本溢出显示省略号
多行文本溢出显示省略号
44 0
|
前端开发 JavaScript
优雅的处理文本溢出截断
文本溢出截断是一个比较常见的场景,如新闻列表页、微博列表、商品列表等,溢出截断主要是为了保证界面的整齐。在《前端开发需要知道的 10 个 CSS 技巧》中有提到过多行文本溢出的处理,本文总结一些优雅的处理文本溢出截断的方法。
128 0
优雅的处理文本溢出截断
|
JavaScript 前端开发
文本溢出显示省略号
文本溢出显示省略号
文本溢出显示省略号

热门文章

最新文章