zoukankan      html  css  js  c++  java
  • iOS富文本(二)初识Text Kit

    概述

    Text Kit 是建立在Core Text上的文本布局系统,虽然没有Core Text那么强大的文本处理功能,但是对于大多数常见的文本布局用Text Kit能够很简单的实现,而不是用Core Text底层的 API去实现。在Text Kit出现以前,UITextView一直是基于WebKit构建的,而iOS7以及以后的版本UITextView都基于Text Kit来构建。下图是摘取苹果官方文档展示了Text KitiOS文本与绘图框架中的位置,可见UILabelUITextFieldUITextView都基于Text Kit构建,而Text KitWebKit是基于Core Text构建的。

    本篇将介绍Text Kit的一些基本结构与用法。


    Text Kit的组件

    • NSTextStorage
      保存管理要显示的文本,NSTextStorageNSAttributeString属性化字符串的子 类。所以NSTextStorage不仅保存文本的内容还有文本的属性信息。
    • NSLayoutManager
      布局管理器,用来管理文本容器的布局
    • NSTextContainer
      表示文本显示的区域,这个区域通常是矩形的,但通过创建NSTextContainer的子类可以描述文本显示的区域为圆形,五边形或其他不规则的形状等。

    每一个NSTextStorage对象有多个 NSLayoutManager对象,每个NSLayoutManager有多个NSTextContainer对象,所以对于指定的文本可以同时有多个布局,每种布局又可以有多个显示的区域。

    下图摘自苹果官方文档显示了Text Kit中数据在每个组件中的流向

    NSTextStorage对象存储的文本信息在NSTextContainer定义的区域范围内由 Text views对象来展示,由NSLayoutManager对象控制布局。


    Text Kit 组件使用

    每一个UITextView对象都有一个NSTextStorage对象,对应一个NSLayoutManager对象与一个NSLayoutManager对象。每当UITextView对象中的文字发生变化时,NSLayoutManager都会监听到并且根据NSTextContainer提供的绘制区域进行绘制,当布局完成,文本的当前显示状态被设为无效,然后文本管理器将排版好的文本设给文本视图。
    下面通过一个简单的代码来描述UITextView这三个组件是如何工作的。
    通过Stroyboard创建一个UITextView对象与一个UIView对象

    创建UIView对象的类,下面的TextKitView类就是上图中青色的视图

    @interface TextKitView ()
    @property (weak,nonatomic) IBOutlet UITextView * textView;
    @end
    @implementation TextKitView
    
    -(void)awakeFromNib
    {
        self.textView.layoutManager.delegate = self;  
        //设置NSLayoutManager的代理每当textView内容变化时都会重新布局并且在布局结束后出发代理方法
    }
    - (void)layoutManagerDidInvalidateLayout:(NSLayoutManager *)sender
    {
        [self setNeedsDisplay];
        //每次textView内容变化时对TextKitView进行重绘
    }
    -(void)drawRect:(CGRect)rect
    {
        NSRange range = [self.textView.layoutManager glyphRangeForTextContainer:self.textView.textContainer];
        //得到绘制的范围
        [self.textView.layoutManager drawGlyphsForGlyphRange:range atPoint:CGPointMake(10, 10)];
        //开始绘制
    }
    -(void)layoutSubviews
    {
        self.textContainer.size = self.frame.size;
        // 设置绘制区域
    }
    @end

    实现效果

    每当textView中的文字发生变化时青色视图中的文字也会同步改变

    多容器布局

    由于每个NSTextStorage对象对应多个NSLayoutManager对象,所以单独创建一个NSLayoutManager对象来管理在上面代码中青色视图中的文字显示。

    @interface TextKitView ()<NSLayoutManagerDelegate>
    @property (weak,nonatomic) IBOutlet UITextView * textView;
    @property (nonatomic,strong) NSTextContainer *textContainer;
    @property (nonatomic,strong) NSLayoutManager *layoutManager;
    @end
    @implementation TextKitView
    
    -(void)awakeFromNib
    {
        self.textContainer = [[NSTextContainer alloc] init];
        self.layoutManager = [[NSLayoutManager alloc] init];
        self.layoutManager.delegate = self;
        [self.layoutManager addTextContainer:self.textContainer];
        [self.textView.textStorage addLayoutManager:self.layoutManager];
    }
    - (void)layoutManagerDidInvalidateLayout:(NSLayoutManager *)sender
    {
        [self setNeedsDisplay];
    }
    -(void)drawRect:(CGRect)rect
    {
        NSLayoutManager *layoutManager = self.layoutManager;
        NSRange range = [layoutManager glyphRangeForTextContainer:self.textContainer];;
        [layoutManager drawGlyphsForGlyphRange:range atPoint:CGPointMake(10, 10)];
    }
    -(void)layoutSubviews
    {
        self.textContainer.size = self.frame.size;
    }
    
    @end

    上面代码实现的效果与前一个例子相同,不同的是重新创建了一个NSLayoutManager对象添加到textViewNSTextStorage对象中,新创建一个NSTextContainer对象添加到NSLayoutManager对象中。所以显示的内容是一样的但是拥有不同的容器布局。
    代码示例在github 多容器布局 Tag下载


    路径排除

    Text Kit可以实现在文本布局中排除一些区域让这些区域不显示文字,这对于一些图文混排的效果能够很轻松的实现。Text Kit路径排除主要是用贝塞尔曲线来排除制定区域的文字,下面通过一段简单的代码来实现路径排除效果

    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UITextView *textView;
    
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        UIBezierPath *path1 = [UIBezierPath bezierPath];
        [path1 addArcWithCenter:CGPointMake(50 ,50)
                        radius:20
                    startAngle:0
                      endAngle:M_PI*2
                     clockwise:NO];   //(50,50)为圆心20为半径的圆
        [path1 closePath];
    
        UIBezierPath *path2 = [UIBezierPath bezierPath];
        [path2 addArcWithCenter:CGPointMake(100 ,100)
                         radius:20
                     startAngle:0
                       endAngle:M_PI*2
                      clockwise:NO];  //(100,100)为圆心20为半径的圆
        [path2 closePath];
        self.textView.textContainer.exclusionPaths = @[path1,path2]; //设置要排除的路径
        // Do any additional setup after loading the view, typically from a nib.
    }
    @end

    实现效果

    代码示例可以在github 路径排除 Tag下载


  • 相关阅读:
    知识积累
    路由层
    数据表记录的增删改查
    连接数据库
    django数据请求
    力扣(LeetCode) 509. 斐波那契数
    力扣(LeetCode)69. x 的平方根
    力扣(LeetCode) 104. 二叉树的最大深度
    力扣(LeetCode) 27. 移除元素
    力扣(LeetCode) 136. 只出现一次的数字
  • 原文地址:https://www.cnblogs.com/itrena/p/5927104.html
Copyright © 2011-2022 走看看