boundingRectWithSize以及TextKit文字环绕

通过2个例子,复习下textview的相关知识

一.文字计算大小

NSString的这个函数:

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(nullable NSDictionary *)attributes context:(nullable NSStringDrawingContext *)context NS_AVAILABLE(10_11, 7_0);

用法:

-(void) countBounding{
    NSString * txt = @"大多来问我的朋友们其实都不是很清楚自己的定位,要么是简单的告诉我高考英语考了多少(亲爱的江苏卷的135和全国二卷的135并不是一个层次的好么……),要么简单说一句觉得自己英语很差。只有两个月你给我一句我英语基础不好,怎么才能过四六级,我真的不是神回答不出来的。所以,第一步,也是最最重要的一步,买一本四六级真题(个人买过星火英语的那个黑皮系列卷,不管什么国内的英语考试都可以来一套,很不错,安利一下)。";
    
    
    CGSize size = CGSizeMake(100, 1000);
    // CGSize size = CGSizeMake(1000, 100);
    CGRect rect =  [txt boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:14]} context:nil];
    NSLog(@"rect:s%f, %f",rect.size.width, rect.size.height);
    
    self.textView.text = txt;
    self.textView.frame = CGRectMake( 0, 0, ceil(rect.size.width), ceil(rect.size.height));
}

重要参数:

size:
通常,计算出来的区域是以传入的size的宽度为准则的.在宽度填满的基础上,再计算高度.(文档说的)
所以把我们需要的宽度传入,然后高度写的尽量大,写小了也没关系.

options:

  • NSStringDrawingUsesLineFragmentOrigin 这个必写,文档说的

  • NSStringDrawingUsesFontLeading 计算行高时使用行距。(字体大小+行间距=行距)

  • NSStringDrawingUsesDeviceMetrics 计算时用象形文字的边框而不是印刷字体的边框

  • NSStringDrawingTruncatesLastVisibleLine 如果传入的size装不下所有文本,文本会被省略号结尾.前提是NSStringDrawingUsesLineFragmentOrigin必写,外加String的line break mode必须是NSLineBreakByWordWrapping或者NSLineBreakByCharWrapping

  • attributes 需要的NSAttributeString的特性

附上一张重要的图,说明NSAttributeString的某些属性,来源于官网:

boundingRectWithSize以及TextKit文字环绕_第1张图片
1-1

二. 文字环绕图片

1. 吃点入门概念

如此好的官网不得不看!

TextKit目前和WebKit平级,上面是三个我们熟悉的文本显示控件


boundingRectWithSize以及TextKit文字环绕_第2张图片
2-1
  • NSTextContrainer : 规定了文字要画的矩形(可以是任意形状,NSBezierPath) ,它是UITextView等的属性
  • NSTextStorage : 它是NSMutableAttributedString的子类,它规定了要写的text,以及它们的显示属性
  • NSLayoutManager : 负责把Unicode文字变成象形文字.


    boundingRectWithSize以及TextKit文字环绕_第3张图片
    2-2

光看概念和框架只会得到一头雾水.上代码:

-(void ) testAttribute{
    
    NSString * str = @"三个月说一门流利外语没问题,只要你每天不断练习听力、不断模仿着说,尽量学一些贴近生活的句子,毕竟学语言的目的不仅仅是为了考试,更多运用到生活之中。语言学习需要一小时、一小时的积累,不是大家想象中那样几天就可以掌握一门外语。这一点我很赞同,当初自己在学德语时,也是花了一段时间才能够顺利开口说句子。毕竟,你得花时间去学音标、音素,还得花时间去背单词,才会有接下来的开口说.";
    
    
    NSString * str1 = @"培养自制力是一个漫长艰苦的过程。太多的诱惑随时可能让你功亏一篑。所谓的坚持,是心中纠结疑惑,还是继续在往前走,是自己战胜自己的过程。究竟怎样才能排除诸多干扰呢?其实关键就是要学会心理暗示,提前设想那些诱惑带来的种种负面影响,然后在心中一遍又一遍重复告诉自己。";
    
    NSTextStorage * textStorage = [[NSTextStorage alloc] initWithString:str];
    
    
    CGRect rect = CGRectInset(self.view.bounds, 10, 20);
    
    
    //这一段代码很好的诠释了三者的纠结关系,自己体会-----------
    NSLayoutManager * layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];
    NSTextContainer * textContainer = [[NSTextContainer alloc] initWithSize:rect.size];
    [layoutManager addTextContainer:textContainer];
    UITextView * textView = [[UITextView alloc] initWithFrame:rect textContainer:textContainer];
    //-----------
    
    [self.view addSubview:textView];
    
    //官网说这个必须写
    [textStorage beginEditing];

    //1. 通过AttributedString  设置attribute的属性 -- 这里最好写在textStorage初始化的时候,不然就赋值2次string了.是吧.而且实践得知,这样做显示出来非常慢,估计是因为替换了显示的文字的缘故
    
    //这句话给文字赋上了一种风格
    NSDictionary *attrsDic = @{NSTextEffectAttributeName: NSTextEffectLetterpressStyle};
    NSMutableAttributedString * attributeStr = [[NSMutableAttributedString alloc] initWithString:str1 attributes:attrsDic];
    [textStorage setAttributedString:attributeStr];

    
    //2. 通过对textStorage的addAttribute:value:range来后期设置属性
    // 这个让前三个字变红
    [textStorage addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 3)];

    
    [textStorage endEditing];
}

2. 文字环绕

文字环绕其实就是运用了UITextStorage可以设置任意形状这一特性.它有个exclusionPaths属性,是排除文字的区域.官网这图很形象的告诉我们exclusionPaths存在时,文字的具体表现:

boundingRectWithSize以及TextKit文字环绕_第4张图片
2-1

需要注意的是传入的rect必须是相对于textview的rect,而不是图片的frame.
用UIView的函数转换:

- (CGRect)convertRect:(CGRect)rect fromView:(nullable UIView *)view;

直接上代码:

-(void ) testSurround{
    
    NSString * str = @"三个月说一门流利外语没问题,只要你每天不断练习听力、不断模仿着说,尽量学一些贴近生活的句子,毕竟学语言的目的不仅仅是为了考试,更多运用到生活之中。语言学习需要一小时、一小时的积累,不是大家想象中那样几天就可以掌握一门外语。这一点我很赞同,当初自己在学德语时,也是花了一段时间才能够顺利开口说句子。毕竟,你得花时间去学音标、音素,还得花时间去背单词,才会有接下来的开口说.清朝诗人王永彬曾说:身无饥寒,父母不曾亏我;人无长进,我以何对父母。努力,很多时候不是为了和别人竞争,只是因为我努力就会有收获,没有那么惊天动地,却可以给我带来更丰厚的一笔报酬,给老爸老妈买一件喜欢的衣服,世界那么大,我想带他们去看看。或许努力了依旧过不好自己的一生,改变不了这个社会,但至少,因为努力,我可以成为他们的依靠。或许这就是我们所有普通人努力的意义,为了更有尊严的活着,为了拥有更多选择的机会,为了遇见更好的自己,为了给爱我们的人一点回馈。";
    
    
    UIView* innerView = [[UIView alloc] initWithFrame:CGRectMake(100,100,100, 100)];
    
    [self.view addSubview:innerView];
    
    
    NSTextStorage * textStorage = [[NSTextStorage alloc] initWithString:str];
    
    
    CGRect rect = CGRectInset(self.view.bounds, 10, 20);
    
    
    //这一段代码很好的诠释了三者的纠结关系,自己体会-----------
    NSLayoutManager * layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];
    NSTextContainer * textContainer = [[NSTextContainer alloc] initWithSize:rect.size];
    [layoutManager addTextContainer:textContainer];

    //-----------
    

    UITextView* textView = [[UITextView alloc] initWithFrame:self.view.bounds textContainer:textContainer];
    
    [self.view insertSubview:textView belowSubview:innerView];
    
    //官网说这个必须写
    [textStorage beginEditing];
    
    //....
    
    [textStorage endEditing];
    
    CGRect relativeRect = [textView convertRect:innerView.frame fromView:self.view];
    UIBezierPath * path =  [UIBezierPath bezierPathWithRect:relativeRect];
    textView.textContainer.exclusionPaths = @[path];
    innerView.backgroundColor = [UIColor redColor];
}

效果:

boundingRectWithSize以及TextKit文字环绕_第5张图片
2-2

Demo

你可能感兴趣的:(boundingRectWithSize以及TextKit文字环绕)