参考、有demo:https://www.objc.io/issues/5-ios7/getting-to-know-textkit/
![](https://img.haomeiwen.com/i4188482/9b755d110f17aa16.png)
![](https://img.haomeiwen.com/i4188482/6037ee174c8c5569.png)
![](https://img.haomeiwen.com/i4188482/bf462c7341886777.png)
![](https://img.haomeiwen.com/i4188482/1674a6b38e5dcd59.png)
NSTextContainer、NSLayoutManager、NSTextStorage及其相互关系:
![](https://img.haomeiwen.com/i4188482/c88c2d0279818559.png)
- NSTextStorage保存并管理UITextView要展示的文字内容,该类是NSMutableAttributedString的子类,由于可以灵活地往文字添加或修改属性,所以非常适用于保存并修改文字属性。
- NSLayoutManager用于管理NSTextStorage其中的文字内容的排版布局。
- NSTextContainer则定义了一个矩形区域用于存放已经进行了排版并设置好属性的文字。
Glyph 字型
- Glyph = Character + font
- CGGlyph
- Glyph由NSLayoutManager控制
通过Glyph可以准确定位文字在视图中的位置,所以例如下划线、文字背景色等都是通过Glyph实现的。
![](https://img.haomeiwen.com/i4188482/3dbc16a3b38d999d.png)
分页显示
用两个TextView显示NSTextContainer,系统会自动处理好文字的显示
![](https://img.haomeiwen.com/i4188482/e130550d6fbc676f.png)
对link作处理
![](https://img.haomeiwen.com/i4188482/4d44febf6b0c3178.png)
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
{
// Normal replace
[_imp replaceCharactersInRange:range withString:str];
[self edited:NSTextStorageEditedCharacters range:range changeInLength:(NSInteger)str.length - (NSInteger)range.length];
// Regular expression matching all iWords -- first character i, followed by an uppercase alphabetic character, followed by at least one other character. Matches words like iPod, iPhone, etc.
static NSDataDetector *linkDetector;
linkDetector = linkDetector ?: [[NSDataDetector alloc] initWithTypes:NSTextCheckingTypeLink error:NULL];
// Clear text color of edited range
NSRange paragaphRange = [self.string paragraphRangeForRange: NSMakeRange(range.location, str.length)];
[self removeAttribute:NSLinkAttributeName range:paragaphRange];
[self removeAttribute:NSBackgroundColorAttributeName range:paragaphRange];
[self removeAttribute:NSUnderlineStyleAttributeName range:paragaphRange];
// Find all iWords in range
[linkDetector enumerateMatchesInString:self.string options:0 range:paragaphRange usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
// Add red highlight color
[self addAttribute:NSLinkAttributeName value:result.URL range:result.range];
[self addAttribute:NSBackgroundColorAttributeName value:[UIColor yellowColor] range:result.range];
[self addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:result.range];
}];
}
代码示例
//
// ViewController.m
// safeArea
//
// Created by liboxiang on 2018/4/25.
// Copyright © 2018年 liboxiang. All rights reserved.
//
#import "ViewController.h"
#import "SecondViewController.h"
#import "TextStorage.h"
@interface ViewController ()
@property (strong, nonatomic) NSTextStorage *textStorage;
@property (strong, nonatomic) UITextView *textView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *pushButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 70, 80, 35)];
[pushButton setTitle:@"push" forState:UIControlStateNormal];
[pushButton addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pushButton];
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
[self setupTextView];
}
- (void)setupTextView
{
CGRect textViewRect = CGRectInset(self.view.bounds,10.0,150.0);
// NSTextContainer
NSTextContainer * container = [[NSTextContainer alloc] initWithSize:CGSizeMake(textViewRect.size.width,CGFLOAT_MAX)]; // iOS 7.0中的新增内容
container.widthTracksTextView = YES; //控制当其文本视图被调整大小时,接收是否调整其边界矩形的宽度
//设置贝塞尔曲线,文字会绕开曲线包围位置
UIBezierPath *exclusion = [UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 50, 30)];
container.exclusionPaths = @[exclusion];
// NSLayoutManager
NSLayoutManager * layoutManager = [[NSLayoutManager alloc] init]; // iOS 7.0中的新增内容
[layoutManager addTextContainer:container];
// NSTextStorage子类
self.textStorage = [[NSTextStorage alloc] initWithString:@"Alice alsjf lasjdflk aslfkjal; asldfjalsdfj alsdfjals;dfk sdnvlxdnv asldfkjalsdk alsdjflasdf as;fj asdf aslfjals dfas;ldfkjals flasdfjldksjl.dsa jfkladjsf Alice alsdfjklk Rabbit"]; // iOS 7.0中的新增内容
[self.textStorage addLayoutManager:layoutManager];
// UITextView
UITextView * newTextView = [[UITextView alloc] initWithFrame:textViewRect textContainer:container];
newTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
newTextView.scrollEnabled = YES;
newTextView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
// newTextView.editable = NO;
newTextView.font = [UIFont fontWithName:[UIFont familyNames][2] size:18.0];
newTextView.dataDetectorTypes = UIDataDetectorTypeAll;
self.textView = newTextView;
[self.view addSubview:self.textView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchesBegan:)];
[_textView addGestureRecognizer:tap];
}
- (void)buttonAction
{
//修改文字和文字属性
[_textStorage beginEditing];
NSDictionary *attrsDic = @{NSTextEffectAttributeName: NSTextEffectLetterpressStyle};
UIKIT_EXTERN NSString *const NSTextEffectAttributeName NS_AVAILABLE_IOS(7_0); // NSString, default nil: no text effect
NSMutableAttributedString *mutableAttrString = [[NSMutableAttributedString alloc] initWithString:@"Letterpress" attributes:attrsDic];
NSAttributedString *appendAttrString = [[NSAttributedString alloc] initWithString:@" Append:Letterpress"];
[mutableAttrString appendAttributedString:appendAttrString];
[_textStorage setAttributedString:mutableAttrString];
[_textStorage endEditing];
}
- (void)touchesBegan:(UITapGestureRecognizer *)sender {
//获取点击的字体
if (self.textView.isFirstResponder) {
return;
}
CGPoint touchPoint = [sender locationInView:self.textView];
NSUInteger charIndex = [self.textView.layoutManager
characterIndexForPoint:touchPoint
inTextContainer:self.textView.textContainer
fractionOfDistanceBetweenInsertionPoints:0];
NSLog(@"%@",[_textView.text substringWithRange:NSMakeRange(charIndex, 1)]);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
创建NSTextStorage子类
必须重写一下四个方法
-string
-attributesAtIndex:effectiveRange:
-replaceCharactersInRange:withString:
-setAttributes:range:
通知
NSTextStorageWillProcessEditingNotification
UIContentSizeCategoryDidChangeNotification
这是监听系统字体改变的,如果设置了监听,则当在手机设置里面修改字体大小的时候可以接收到消息。
网友评论