有关UILabel的行高设置的探索
主题 《iOS开发》 by evan
导语现在的UI,为了展现自己的独特的设计风格,总会“想方设法”的的“为难”开发。其中一项最基本的就是设计UILabel的行高。很多开发都会说,要设置行高,论文,简单啊,设置attributedText就可以,如果深入去探索,会发现,并没有想象的那么简单。
需求如果有这么一个UI需求
要做的第一步肯定的去设置行高,那么下面我们就来设置一下行高。
1. 创建NSMutableAttributedString设置行高直接上代码
NSRange range = NSMakeRange(0, [text length]); NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text]; NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setLineSpacing:12.0];//调整行间距 [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; [attributedString addAttribute:NSFontAttributeName value:label.font range:range]; label.attributedText = attributedString;这里按照UI效果图,设置LineSpacing为12,从16号字到20号字,中英文显示效果如下:
问题:虽然我们按照了效果图设置了行高为12,但是为啥实际效果,行高明显大于12呢?2. 解决行高大于12的问题出现这个问题,有可能有两个因素,要么是UI标注出错了,要么是代码设置出错了。经过验证,我发现UI标注并没有问题,代码也没问题。
问题出在UILabel上,回忆一下,在我们没有设置行高的年代,UIlabel会有一个默认的行高,经过测量,没设置行高的时候,默认行高大概在3px ~ 4px 之间,那么我就直接设置LineSpacing为8。
效果如下:
2. 现在有一个新的需求,最多显示两行听到这个需求,觉得简单啊,那就来吧,设置numberOfLines为2不OK了。
事实如此吗?
问题:很奇怪,跟想象的完全不一样,是吧?文字显示的是2行,但是占用了整个UIlabel需要的高度代码里是这样计算Label高度的:
NSDictionary *dic = @{NSFontAttributeName:label.font, NSParagraphStyleAttributeName:paragraphStyle}; NSStringDrawingOptions options = NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading; CGFloat height = [text boundingRectWithSize:CGSizeMake(_scrollView.bounds.size.width - 60, CGFLOAT_MAX) options:options attributes:dic context:nil].size.height;首先在UILabel.h里可以看到:
// the underlying attributed string drawn by the label, if set, the label ignores the properties above. @property(nullable, nonatomic,copy) NSAttributedString *attributedText NS_AVAILABLE_IOS(6_0); // default is nil一旦设置了attributedText,以下这一堆属性将无效:
@property(nullable, nonatomic,copy) NSString *text; // default is nil @property(null_resettable, nonatomic,strong) UIFont *font; // default is nil (system font 17 plain) @property(null_resettable, nonatomic,strong) UIColor *textColor; // default is nil (text draws black) @property(nullable, nonatomic,strong) UIColor *shadowColor; // default is nil (no shadow) @property(nonatomic) CGSize shadowOffset; // default is CGSizeMake(0, -1) -- a top shadow @property(nonatomic) NSTextAlignment textAlignment; // default is NSTextAlignmentLeft @property(nonatomic) NSLineBreakMode lineBreakMode; // default is NSLineBreakByTruncatingTail. used for single and multiple lines of text我们可以看到lineBreakMode也在里面,那我们就在NSMutableParagraphStyle里设置lineBreakMode :
[paragraphStyle setLineBreakMode:label.lineBreakMode];运行后,Label的高度还是没什么改变,开题报告,依旧是只显示2行,但是占用了所有高度。
问题应该是在boundingRectWithSize这个方法里,因为这里没有提供任何参数让我们设置UILabel的显示行数,所以,它计算的高度是整个Text需要显示的高度。
OK,既然如此我就换一个计算行高的方式:
CGSize size = [label sizeThatFits:CGSizeMake(_scrollView.bounds.size.width - 60, CGFLOAT_MAX)]; CGFloat height = size.height;效果如下:
问题: 中文在显示一行的时候,文字底边距离UIlabel的下边,仍然有一个高度,这是怎么回事?3. 探索单行中文底边距的问题#p#分页标题#e#进入NSMutableParagraphStyle.h,看看有那么可以设置的属性:
@property(NS_NONATOMIC_IOSONLY) CGFloat lineSpacing; @property(NS_NONATOMIC_IOSONLY) CGFloat paragraphSpacing; @property(NS_NONATOMIC_IOSONLY) NSTextAlignment alignment; @property(NS_NONATOMIC_IOSONLY) CGFloat firstLineHeadIndent; @property(NS_NONATOMIC_IOSONLY) CGFloat headIndent; @property(NS_NONATOMIC_IOSONLY) CGFloat tailIndent; @property(NS_NONATOMIC_IOSONLY) NSLineBreakMode lineBreakMode; @property(NS_NONATOMIC_IOSONLY) CGFloat minimumLineHeight; @property(NS_NONATOMIC_IOSONLY) CGFloat maximumLineHeight; @property(NS_NONATOMIC_IOSONLY) NSWritingDirection baseWritingDirection; @property(NS_NONATOMIC_IOSONLY) CGFloat lineHeightMultiple; @property(NS_NONATOMIC_IOSONLY) CGFloat paragraphSpacingBefore; @property(NS_NONATOMIC_IOSONLY) float hyphenationFactor; @property(null_resettable, copy, NS_NONATOMIC_IOSONLY) NSArray<NSTextTab *> *tabStops NS_AVAILABLE(10_0, 7_0); @property(NS_NONATOMIC_IOSONLY) CGFloat defaultTabInterval NS_AVAILABLE(10_0, 7_0); @property(NS_NONATOMIC_IOSONLY) BOOL allowsDefaultTighteningForTruncation NS_AVAILABLE(10_11, 9_0);可设置的属性并不是很多,不过可一看到一个minimumLineHeight 和 maximumLineHeight 。至此有了一个思考。最小行高应该是font所需要的行高,最高行高应该是font所需行高 + lineSpacing。
于是做如下设置:
[paragraphStyle setMinimumLineHeight:label.font.lineHeight]; [paragraphStyle setMaximumLineHeight:label.font.lineHeight + 8.0];运行效果如下:
问题:多行 跟 单行的效果不统一,多行的时候底边没有边距,单行的时候底边有边距。这也不是我们所需要的效果,怎么办?......
......
......
在我研究了多天之后,依然对此无解
不过有两个方案可以解决这个问题:
1.判断UILabel的行数,从而来改变UIlabel下面控件与它的间距:
BOOL isMultiLine = size.height > (label.font.lineHeight + MAX(label.font.lineHeight, 8)) ? YES : NO; // 判断UIlabel的行数 if (isMultiLine) { ... // 设置UIImageView.top = 10; } else { ... // 设置UIImageView.top = 10 - 8; }2.采用CoreText去自定义Label,里面你可以畅所欲为,控制你想控制的一切。
如果有iOS技术达人,有这方面的经验,希望可以跟您交流,共同进步!!!