iOS开发iOS Swift && Objective-CCoretext

iOS Text Part1:TextKit

2016-07-21  本文已影响423人  破弓

0.TextKit包含类讲解

TextKit_1.png
TextKit_2.png

如图TextKit_1可以看到,我们一般能接触到的文字控件全是由TextKit封装而成的。

TextKit内的三个类功能如下:
NSTextStorage:保存文字控件要显示的NSAttributedString
NSLayoutManager:掌控文字控件对文字的具体绘制操作
NSTextContainer:把控文字控件的显示的区域

隶属关系如下:

 self.textStorage = [[NSTextStorage alloc] init];
 self.textLayoutManager = [[NSLayoutManager alloc] init];
 self.textContainer = [[NSTextContainer alloc]init];
    
 [self.textStorage addLayoutManager:self.textLayoutManager];
 [self.textLayoutManager addTextContainer:self.textContainer];
    
 self.textLayoutManager.delegate = self;

1.开发时的应用

TextKitTrain源码

1.1 同一段文字分View显示

Simulator Screen Shot 2016年7月20日 下午7.54.28.png

主角:NSTextStorage####

如图TextKit_2可以看到,NSTextStorage、NSLayoutManager 和 NSTextContainer 之间的箭头都是有两个头的。我试图描述它们的关系是 1 对 N 的关系。就是那样:一个 Text Storage 可以拥有多个 Layout Manager,一个 Layout Manager 也可以拥有多个 Text Container。这些多重性带来了很好的特性。
很有用的一个例子,基于页面的布局:
页面上有多个 Text View ,每个Text View 的 Text Container 都引用同一个 Layout Manager,一个 Text Storage持有这个 Layout Manager,这时这个 Text Storage 就可以将文本分布到多个视图上来显示。

1.2 文字内不规则的区域留白

Simulator Screen Shot 2016年7月20日 下午7.54.05.png

主角:NSTextContainer####

 CGRect ovalFrame = [_textView convertRect:_panView.bounds fromView:_panView];
 // Simply set the exclusion path
 UIBezierPath * ovalPath = [UIBezierPath bezierPathWithOvalInRect:ovalFrame];
 _textView.textContainer.exclusionPaths = @[ovalPath];
 [_textView setNeedsDisplay];

拿到混入图片的矩形坐标,由矩形坐标获取画圆的贝赛尔曲线,将这个贝赛尔曲线赋值给textContainer.exclusionPaths,很简单的就在正常的显示区域内剪出个"洞"。

1.3 Label实现链接点击的效果

Simulator Screen Shot 2016年7月20日 下午8.31.28.png

主角:NSLayoutManager####

before.png
select ing.png

上述为点击高亮前后的显示
两副场景如果是"单独"显示,用attributedString+UILabel应该谁都会,难点就在两副场景的瞬间切换,切换不同的attributedString

1.3.1 预先标定高亮区域

UI层面预先标定:
特殊区域文字颜色与普通文字颜色不同,通过[attributedString addAttribute:NSForegroundColorAttributeName value:range:]的方式来实现

数据层面预先标定:
@property (nonatomic, copy) NSMutableArray * selectableRanges;
label设置selectableRanges属性用于保存所有特殊区域的range

1.3.2 点击时识别是否在高亮区域

 NSUInteger touchedChar = [self.textLayoutManager glyphIndexForPoint:location inTextContainer:self.textContainer];

获取"点击点"所点中字符的index,然后遍历selectableRanges内的每一个range,看是否有哪个range包含了这个index

1.3.3 在高亮区域,切换Label的attributedString

-(void)updateShowTextWithRange:(NSRange)range color:(UIColor *)color
{
    NSMutableAttributedString * muAttr = [self.attributedText mutableCopy];
    if (range.length <= 0) {
        [muAttr removeAttribute:NSBackgroundColorAttributeName range:self.selectRange];
    }else{
        [muAttr addAttribute:NSBackgroundColorAttributeName value:color range:range];
    }
    self.attributedText = [muAttr copy];
    self.selectRange = range;
}

通过[attributedString addAttribute: NSBackgroundColorAttributeName value:range:]的方式更新点中range背景色,然后重新绘制

- (void)drawTextInRect:(CGRect)rect
{
    CGPoint textOffset;
    NSRange glyphRange = [self.textLayoutManager glyphRangeForTextContainer:self.textContainer];
    textOffset = [self calcTextOffsetForGlyphRange:glyphRange];
    //绘制背景
    [self.textLayoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textOffset];
    //绘制字符
    [self.textLayoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textOffset];
}

学习来源:
http://objccn.io/issue-5-1/
https://github.com/m1entus/MZSelectableLabel
上一篇 下一篇

猜你喜欢

热点阅读