Text Kit学习日记–03–影响分页的因素

Overview

前言

第二篇中提到,TextKit的分页效率太低,但是用基于TextKit回炉的UITextView来显示文字的效率还是比较好的,所以可以考虑用CoreText来分页,用TextKit显示结果,关键在于统一两者使用的字体,文本布局,在UIKit的NSAttribuedString.h中定义了大量的键,这里我们可以调整所有可用的属性,如下

 1 NSFontAttributeName NS_AVAILABLE_IOS(6_0); 
 2// UIFont, default Helvetica(Neue) 12
 3
 4 NSParagraphStyleAttributeName NS_AVAILABLE_IOS(6_0); 
 5// NSParagraphStyle, default defaultParagraphStyle
 6
 7 NSForegroundColorAttributeName NS_AVAILABLE_IOS(6_0); 
 8// UIColor, default blackColor
 9
10 NSBackgroundColorAttributeName NS_AVAILABLE_IOS(6_0); 
11// UIColor, default nil: no background
12
13 NSLigatureAttributeName NS_AVAILABLE_IOS(6_0); 
14// NSNumber containing integer, default 1: default ligatures, 0: no ligatures
15
16 NSKernAttributeName NS_AVAILABLE_IOS(6_0); 
17// NSNumber containing floating point value, in points; amount to modify default kerning. 0 means kerning is disabled.
18
19 NSStrikethroughStyleAttributeName NS_AVAILABLE_IOS(6_0); 
20// NSNumber containing integer, default 0: no strikethrough
21
22 NSUnderlineStyleAttributeName NS_AVAILABLE_IOS(6_0); 
23// NSNumber containing integer, default 0: no underline
24
25 NSStrokeColorAttributeName NS_AVAILABLE_IOS(6_0); 
26// UIColor, default nil: same as foreground color
27
28 NSStrokeWidthAttributeName NS_AVAILABLE_IOS(6_0); 
29// NSNumber containing floating point value, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0)
30
31 NSShadowAttributeName NS_AVAILABLE_IOS(6_0); 
32// NSShadow, default nil: no shadow
33
34NSTextEffectAttributeName NS_AVAILABLE_IOS(7_0); 
35// NSString, default nil: no text effect
36
37NSAttachmentAttributeName NS_AVAILABLE_IOS(7_0); 
38// NSTextAttachment, default nil
39
40NSLinkAttributeName NS_AVAILABLE_IOS(7_0); 
41// NSURL (preferred) or NSString
42
43NSBaselineOffsetAttributeName NS_AVAILABLE_IOS(7_0);
44// NSNumber containing floating point value, in points; offset from baseline, default 0
45
46NSUnderlineColorAttributeName NS_AVAILABLE_IOS(7_0); 
47// UIColor, default nil: same as foreground color
48
49NSStrikethroughColorAttributeName NS_AVAILABLE_IOS(7_0); 
50// UIColor, default nil: same as foreground color
51
52NSObliquenessAttributeName NS_AVAILABLE_IOS(7_0); 
53// NSNumber containing floating point value; skew to be applied to glyphs, default 0: no skew
54
55NSExpansionAttributeName NS_AVAILABLE_IOS(7_0); 
56// NSNumber containing floating point value; log of expansion factor to be applied to glyphs, default 0: no expansion
57
58NSWritingDirectionAttributeName NS_AVAILABLE_IOS(7_0); 
59// NSArray of NSNumbers representing the nested levels of writing direction overrides as defined by Unicode LRE, RLE, LRO, and RLO characters. The control characters can be obtained by masking NSWritingDirection and NSTextWritingDirection values. LRE: NSWritingDirectionLeftToRight|NSTextWritingDirectionEmbedding, RLE: NSWritingDirectionRightToLeft|NSTextWritingDirectionEmbedding, LRO: NSWritingDirectionLeftToRight|NSTextWritingDirectionOverride, RLO: NSWritingDirectionRightToLeft|NSTextWritingDirectionOverride,
60
61 NSVerticalGlyphFormAttributeName NS_AVAILABLE_IOS(6_0); 
62// An NSNumber containing an integer value. 0 means horizontal text. 1 indicates vertical text. If not specified, it could follow higher-level vertical orientation settings. Currently on iOS, it's always horizontal. The behavior for any other value is undefined.

由于我的目标是完善阅读器的分页,所以需要考虑的只有字体样式,段落样式,将UITextView的

textContainerInset置零,并将textContainer的lineFragmentPadding置零,然后其余都可以按照默认设置,首先来看看段落样式。

NSParagraphStyle

iOS中各种对象的都拥有默认值,其中NSParagraphStyle的默认值如下

 1Alignment 4,对齐方式,使用默认对齐方式,也就是向左对齐
 2LineSpacing 0,行间距,默认为0,This value is included in the line fragment heights in layout manager.
 3ParagraphSpacing 0, 段落间距:本段落末尾与下一段落顶部的距离,默认为0
 4ParagraphSpacingBefore 0, 段落间距:本段落的顶部与上一段落的底部的距离,默认为0
 5HeadIndent 0, 每行首部与给定margin的距离
 6TailIndent 0, 每行尾部与给定margin的距离
 7FirstLineHeadIndent 0, 段落首行与给定margin的距离,即首行缩进
 8LineHeight 0/0, 
 9LineHeightMultiple 0, 
10LineBreakMode 0, 断行方式,默认按字断行,也就是一次填充一个汉字或单词,剩余空间无法容纳是就断行
11Tabs (
12    28L,
13    56L,
14    84L,
15    112L,
16    140L,
17    168L,
18    196L,
19    224L,
20    252L,
21    280L,
22    308L,
23    336L
24), 
25DefaultTabInterval 0, 
26Blocks (null), 
27Lists (null), 
28BaseWritingDirection -1, 默认书写方式,按照语言规则,中英文是从左到右
29HyphenationFactor 0, 连字符号因素Specifies the threshold for hyphenation,默认为0,使用layout manger来管理连字符号使用,而它的HyphenationFactor默认也为0,不启用连写字符。
30TighteningFactor 0, 
31HeaderLevel 0

NSParagraphStyle的可变子类NSMutableParagraphStyle可以修改各项参数。

UIFontDescriptor

做了几个测试后,发现这个对我完全没有什么卵用。

使用默认方法生成字体对象就好。

其他

有些控件会自己调整行距,比如UITextView,而CATextLayer也有自己的段落样式,同一段由CoreText产生的分页结果,直接提供给CATextLayer基本上会溢出边框,它适合少量的文本,比如在UILabel上添加一层自定义的文本,而使用NSString在上下文中绘图,然后获取位图,传递给CALayer的content则不会发生溢出下一篇,我们记录下各种分页并呈现文字的方式。

第二篇中提到的测试

接下来补上第二篇中有关leading,lineHeight,文本行高度的测试

新建一个SingleView Application,在ViewController中插入以下代码,我使用的是iPhone5 模拟器,iOS8.3,Xcode 6.3.1

1.创建文本,我使用了一本txt格式的小说,编码方式为UTF-8。

2.创建一个UITextView,设置内部textContainer的Inset为0,并设置linePadding为0,这样文本容器完整覆盖整个UITextView。

3.创建字体,分别使用了三种字体,第一种为中文字体,第二种为英文字体,第三种为系统字体,然后打印字体信息,创建属性化字符串后接着设置UITextView的文本内容,然后取一段包含中英文的文本段,打印内容,并计算指定位置的标记符号所在行的行高。

 1-(void)viewDidLoad
 2{
 3    [super viewDidLoad];
 4    [self.view setBackgroundColor:[UIColor redColor]];
 5    NSString *aText = [NSString stringWithContentsOfFile:_path encoding:NSUTF8StringEncoding error:nil];
 6    CGRect textViewRect = CGRectMake(5, 8, 310, 560);
 7    UITextView *textView = [[UITextView alloc] initWithFrame:textViewRect];
 8    [textView setBackgroundColor:[UIColor colorWithRed:249.0/255 green:234.0/255 blue:188.0/255 alpha:1]];
 9    [textView setUserInteractionEnabled:NO];
10    [textView.textContainer setLineFragmentPadding:0.0f];
11    [textView setTextContainerInset:UIEdgeInsetsZero];
12
13    UIFont *font = [UIFont fontWithName:@"STHeitiSC-Light" size:15];
14    NSLog(@"%@",font);
15    NSLog(@"\nfamilyName:%@ \nfontName:%@ \npointSize:%f \nascender:%f \ndescender:%f \ncapHeight:%f \nxHeight:%f \nlineHeight:%f \nleading:%f",font.familyName,font.fontName,font.pointSize,font.ascender,font.descender,font.capHeight,font.xHeight,font.lineHeight,font.leading);
16
17    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
18    dic[NSFontAttributeName] = font;
19    NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
20    dic[NSParagraphStyleAttributeName] = style;
21    style.lineSpacing = 0.4f;
22    NSAttributedString *str = [[NSAttributedString alloc] initWithString:aText attributes:dic];
23    NSLog(@"%@",[str attributedSubstringFromRange:NSMakeRange(0, 465)]);
24    [textView setAttributedText:str];
25    [self.view addSubview:textView];
26    NSLog(@"%@",[textView.attributedText attributedSubstringFromRange:NSMakeRange(0, 465)]);
27    NSLayoutManager *manager = textView.layoutManager;
28    NSRange ra ;
29    [self printfRect:[manager lineFragmentRectForGlyphAtIndex:445 effectiveRange:&ra]];
30
31    font = [UIFont fontWithName:@"HelveticaNeue-Light" size:15];
32    NSLog(@"\nfamilyName:%@ \nfontName:%@ \npointSize:%f \nascender:%f \ndescender:%f \ncapHeight:%f \nxHeight:%f \nlineHeight:%f \nleading:%f",font.familyName,font.fontName,font.pointSize,font.ascender,font.descender,font.capHeight,font.xHeight,font.lineHeight,font.leading);
33
34    dic = [NSMutableDictionary dictionary];
35    dic[NSFontAttributeName] = font;
36    dic[NSParagraphStyleAttributeName] = style;
37    str = [[NSAttributedString alloc] initWithString:aText attributes:dic];
38    NSLog(@"%@",[str attributedSubstringFromRange:NSMakeRange(0, 465)]);
39    [textView setAttributedText:str];
40    NSLog(@"%@",[textView.attributedText attributedSubstringFromRange:NSMakeRange(0, 465)]);
41    manager = textView.layoutManager;
42    [self printfRect:[manager lineFragmentRectForGlyphAtIndex:445 effectiveRange:&ra]];
43
44    font = [UIFont systemFontOfSize:15];
45    NSLog(@"\nfamilyName:%@ \nfontName:%@ \npointSize:%f \nascender:%f \ndescender:%f \ncapHeight:%f \nxHeight:%f \nlineHeight:%f \nleading:%f",font.familyName,font.fontName,font.pointSize,font.ascender,font.descender,font.capHeight,font.xHeight,font.lineHeight,font.leading);
46    dic = [NSMutableDictionary dictionary];
47    dic[NSFontAttributeName] = font;
48    dic[NSParagraphStyleAttributeName] = style;
49    str = [[NSAttributedString alloc] initWithString:aText attributes:dic];
50    NSLog(@"%@",[str attributedSubstringFromRange:NSMakeRange(0, 465)]);
51    [textView setAttributedText:str];
52    NSLog(@"%@",[textView.attributedText attributedSubstringFromRange:NSMakeRange(0, 465)]);
53    manager = textView.layoutManager;
54    [self printfRect:[manager lineFragmentRectForGlyphAtIndex:445 effectiveRange:&ra]];
55}
56-(void)printfRect:(CGRect)rect
57{
58    NSLog(@"x:%f y:%f width:%f height:%f",rect.origin.x,rect.origin.y,rect.size.width,rect.size.height);
59}
60-(void)printLayout:(UIEdgeInsets) inset
61{
62    NSLog(@"right:%f left:%f top:%f bottom:%f",inset.right,inset.left,inset.top,inset.bottom);
63}
64-(BOOL)prefersStatusBarHidden
65{
66    return _isHidden;
67}

当lineSpacing为0时,结果如下

可以看到三种字体中,第一种包含字体最多,第二种最少,第三种次之,由于属性文本打印长度太大,这里暂且略过,它们的终端输出结果如下

 12015-06-01 23:50:12.798 PureReader[6557:280430] 
 2familyName:Heiti SC 
 3fontName:STHeitiSC-Light 
 4pointSize:15.000000 
 5ascender:12.900000 
 6descender:-2.100000 
 7capHeight:11.670000 
 8xHeight:8.865000 
 9lineHeight:15.000000 
10leading:15.000000
112015-06-01 23:50:12.820 PureReader[6557:280430] x:0.000000 y:154.499985 width:310.000000 height:15.450000
122015-06-01 23:50:12.821 PureReader[6557:280430] 
13familyName:Helvetica Neue 
14fontName:HelveticaNeue-Light 
15pointSize:15.000000 
16ascender:14.505000 
17descender:-3.195000 
18capHeight:10.920000 
19xHeight:7.950000 
20lineHeight:17.700001 
21leading:17.700001
222015-06-01 23:50:12.858 PureReader[6557:280430] x:0.000000 y:181.349991 width:310.000000 height:18.135000
232015-06-01 23:50:12.858 PureReader[6557:280430] 
24familyName:.Helvetica Neue Interface 
25fontName:.HelveticaNeueInterface-Regular 
26pointSize:15.000000 
27ascender:14.280000 
28descender:-3.615000 
29capHeight:10.710000 
30xHeight:7.755000 
31lineHeight:17.895000 
32leading:17.895000
332015-06-01 23:50:12.903 PureReader[6557:280430] x:0.000000 y:178.950027 width:310.000000 height:17.895000

可以参照第二篇,第一种,第二种字体的lineSpacing由系统计算,第三种字体没有默认lineSpacing,不支持中文的第二种,第三种字体使用了默认的中文字体,由于中英文混排,文本行高度取行高较大的字体,并以此对文本进行排版;

当字体大小为15时,如果调整lineSpacing,可以发现,lineSpacing小于0.45时对第一种字体不起作用,而对第三种字体起作用;

当保持lineSpacing为0,调整字体大小,可以发现,第一、第二种的文本行高与lineHeight不断加大,系统计算的lineSpacing发生变化,而第三种字体的lineSpacing保持不变。

comments powered by Disqus