关于iOS11中UILabel的问题

很久没来更新博客了,现在终于有点时间来写点东西了。

关于要写的东西,就是目前在项目中遇到的一个小问题,这个坑是苹果给埋的,当然苹果也是出于好意,能让显示的内容更美观。

这个问题就是iOS中UILabel的文字展示的东西。先说说问题从哪里出来的。

 

项目中需要展示一段文字,一句英文,这句英文有长有短,在结尾处要跟一个播放的icon。

要做这个需求,首先想到的是用NSAttributeString来处理做成NSTextAttachment的图片。但是问题是这个icon是一个gif,需要支持动画,那如果用NSTextAttachment来做,一直去改变NSAttributionString的话就很恶心,而且还有一种情况,如果刚好文本填满一行,icon就会换行,所以NSTextAttachment的方案PASS。

那么就普通的UILabel做展示,同层级加一个UIImageView来做animation就够了(P.S UIImageView处理gif其实很不舒服,可以用别的成熟的第三方gif框架)。那么第一个问题来了,如何获取UILabel中最后一个字符的frame呢?万能的google中查到了解决方案:

 1 - (CGRect)characterRectAtIndex:(NSInteger)index {
 2     NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[self attributedText]];
 3     NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
 4     [textStorage addLayoutManager:layoutManager];
 5     NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self bounds].size];
 6     textContainer.lineFragmentPadding = 0;
 7     [layoutManager addTextContainer:textContainer];
 8     NSRange glyphRange;
 9     [layoutManager characterRangeForGlyphRange:NSMakeRange(index, 1) actualGlyphRange:&glyphRange];
10     return [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];
11 }

 

在UILabel的category中加上这个方法,就能获取到了,perfect!

功能完成,继续开发别的功能~

... ...

... ...

... ...

等下,有问题了!

icon位置不对!

写个demo看下`characterRectAtIndex:`方法有啥问题没。

checking...

没问题啊,都没问题啊,为什么就是显示不对呢???

checking...

发现问题了!

明明我的UILabel这行还能放一个单词,但是这个单词居然换到下一行显示了!为啥?咋整?

googleing...

发现Stack Overflow有大佬早就遇到过相同的问题了,在*这篇帖子中,就有提到这个问题,如下图,of换行了!为什么!

关于iOS11中UILabel的问题

 

 

 OK,大佬给了链接,Rags, Widows & Orphans,总结起来,苹果不想让UILabel展示出现“寡妇”单词。

形如下图中左图,最后一个单词all就是一个“寡妇”单词,在段尾且独占一行。寡妇是印刷术中的概念,文章中还介绍了“抹布”和“孤儿”。

 关于iOS11中UILabel的问题

我们现在问题是“寡妇”,就看“寡妇”这个问题吧。“寡妇”这种情况会被认为是一种不美观的排版,因为它在段落之间或页面底部留下了太多的空白,这会打断读者的眼睛并降低可读性。

 所以,苹果就在iOS11中更新了排版的逻辑。

 

但是!自定义NSTextStorage和NSLayoutManager却不会去理会“寡妇”,导致获取字符frame出错!

那怎么办?Stack Overflow中大佬给的解决方案,改用UITextView。emmmmm,那就试试吧,这个地方只能弃用UILabel,改用UITextView。

 1 UITextView *textView = [[UITextView alloc] init];
 2 textView.showsVerticalScrollIndicator = NO;
 3 textView.showsHorizontalScrollIndicator = NO;
 4 textView.editable = NO;
 5 textView.selectable = NO;
 6 textView.backgroundColor = [UIColor clearColor];
 7 textView.textContainer.lineFragmentPadding = 0;
 8 textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
 9 textView.scrollEnabled = NO;
10 textView.bounces = NO;

把UITextView能关的,改关的都关了,做成一个UILabel的样子,build and run。。。成了!

 

最后,我们还是要把icon放上去,同样用NSTextStorage和NSLayoutManager来获取最后一个字符的位置。

 1 - (CGRect)frameOfLastCharacter {
 2     NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.textView.attributedText];
 3     NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
 4     [textStorage addLayoutManager:layoutManager];
 5     NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self.textView bounds].size];
 6     textContainer.lineFragmentPadding = 0;
 7     [layoutManager addTextContainer:textContainer];
 8     NSRange glyphRange;
 9     [layoutManager characterRangeForGlyphRange:NSMakeRange(self.textView.attributedText.string.length - 1, 1) actualGlyphRange:&glyphRange];
10     CGRect sentenceLastCharFrame = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];
11     CGRect frame = [self.textView convertRect:sentenceLastCharFrame toView:self.view];
12     return frame;
13 }

完工!

关于iOS11中UILabel的问题

上一篇:vue-cli3打包app物理按键失效的问题[已解决]


下一篇:rem+媒体查询---移动端 设计稿以375