iOS7以后的富文本

在iOS7以前如果想要做富文本,需要用到CoreText诸如此类的framework和取巧的方法,是一件很费劲的事,虽然有很多封装好的类库,但是由于别人封装的很齐全,我们用到的又只是其中很少的一部分,这就造成了代码的冗余。
在iOS7以后苹果出了一个<TextKit>来解决这种麻烦事,不过,对于一般的使用场景来说的话,下面的方法就够用了。

首先来个例子:

  • 首先在试图控制器上添加一个TextView(当然UITextField、UILabel也行)
1
2
3
4
5
self.textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 200)];
self.textView.delegate = self;
self.textView.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.2];
self.textView.editable = NO;
[self.view addSubview:self.textView];
  • 然后再创建一个包含图片的富文本
1
2
3
4
5
6
7
8
9
10
11
12
- (NSAttributedString *)attachmentStringWithImage:(UIImage *)image
{
NSData *data = UIImageJPEGRepresentation(image, 1.0);
NSTextAttachment *imageAttachment = [[NSTextAttachment alloc] initWithData:data ofType:@"png"];
imageAttachment.image = image;
imageAttachment.bounds = CGRectMake(0, 0, 64, 64);
NSAttributedString *attachAttributeString = [NSAttributedString attributedStringWithAttachment:imageAttachment];
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithAttributedString:attachAttributeString];
[attributeString addAttribute:NSLinkAttributeName value:[NSURL URLWithString:@"http://www.baidu.com"] range:NSMakeRange(0, attributeString.length)];
return attributeString;
}
  • 最后将图片富文本和文字融合在一起
1
2
3
4
5
6
7
8
9
- (NSAttributedString *)generateTestAttributeString
{
NSString *imgTagString = @"[img]";
NSString *oriString = @"我是用来测试的字符串,下面插播一个图片:[img]\n图片过后又是一段文字";
NSAttributedString *imageAttributeString = [self attachmentStringWithImage:[UIImage imageNamed:@"icon"]];
NSMutableAttributedString *attribteString = [[NSMutableAttributedString alloc] initWithString:oriString];
[attribteString replaceCharactersInRange:[oriString rangeOfString:imgTagString] withAttributedString:imageAttributeString];
return attribteString;
}

要想实现超链接点击跳转,需要实现UITextView的一个协议方法:

1
2
3
4
5
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
NSLog(@"%@", [URL absoluteString]);
return YES;
}

下面来测试一下:

1
self.textView.attributedText = [self generateTestAttributeString];

结果如下,点击图片的时候会跳转到Safari中并打开百度:


一、NSTextAttachment文本附件,NS_CLASS_AVAILABLE(10_0, 7_0)

Properties

在制作富文本的时候,使用NSTextAttachment作为一个附件对象,添加到富文本中。
它整个类都没多少东西。

  • contents(NSData*) 附件内容,一般来说就是要添加的图片数据。
  • fileType(NSString*) 附件的类型,png、jpg等
  • image(UIImage*) 要布局到富文本中的图片
  • bounds(CGRect) 图片在富文本中得位置,一般来说point会用于图片位置的偏移,而size则是重要的决定图片的大小
  • - (instancetype)initWithData:(nullable NSData *)contentData ofType:(nullable NSString *)uti
    初始化方法,当然也能使用父类NSObject的初始化方法,其所对应的参数在上面都有说明。
  • NSFileWrapper(NSFileWrapper*) 如果附件实例对象不直接使用图片的话,可以使用这个加载本地的资源。

使用

NSTextAttachment的使用方法在上面已经有了实例了。
看苹果官方的SDK可以发现,它实现了NSTextAttachmentContainer协议,这就说能让我们按别的方法来使用它。
例如,新建一个文件,继承自NSTextAttachment,然后实现代理方法:

1
2
3
4
5
6
7
8
9
10
@implementation AUUTextAttachment
- (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex
{
return CGRectMake(0, 0, 64, 64);
}
- (UIImage *)imageForBounds:(CGRect)imageBounds textContainer:(NSTextContainer *)textContainer characterIndex:(NSUInteger)charIndex
{
return [UIImage imageNamed:@"icon"];
}
@end

其中方法一,返回NSTextAttachment所在位置的bounds
方法二,返回一个在textContainer中由NSLayoutManager渲染的图片。它必须返回一个图片,如果返回的是nil,系统将会默认的根据contentsfileType展现一个相应的占位图像。
这时在使用的时候,只需要初始化一下,就能返回一个attachment:

1
AUUTextAttachment *imageAttachment = [[AUUTextAttachment alloc] init];

当然,这只是一个举例测试,你可以根据你的实际需要来实现符合你自己需求的内容。


二、NSAttributedString富文本

在做字符串处理的时候很常用的一个类,但是直到iOS6以后,结合一些富文本属性才得以受到人们的喜爱,然后到了iOS7以后更多的属性及NSTextAttachment的推出,才让它发挥出更大的作用。
其使用方法跟字符串NSString的操作方法很类似,下面要介绍的是它的一些属性。

Attributes

  • NSFontAttributeName

    字体,值是UIFont类型。

  • NSParagraphStyleAttributeName

    段落属性,NSParagraphStyle

  • NSForegroundColorAttributeName

    字体颜色

  • NSBackgroundColorAttributeName

    背景色

  • NSLigatureAttributeName

    连体字符,是指某些连在一起的字符,它们采用单个的图元符号。值为NSNumber(integer)类型。
    0 表示没有连体字符。
    1 表示使用默认的连体字符。
    2表示使用所有连体符号。
    默认值为 1(iOS不支持值为2)。

  • NSKernAttributeName

    设定文字间距,值为NSNumber(float)类型,正值表示间距变大,负数紧缩。

  • NSStrikethroughStyleAttributeName

    删除线,值为NSUnderlineStyle枚举类型。

  • NSUnderlineStyleAttributeName

    文字内容底部的下划线,值为NSUnderlineStyle枚举类型。

  • NSStrokeColorAttributeName

    镂空的文字内容描边的颜色

  • NSStrokeWidthAttributeName

    用于改变文字描边宽度(相对于字体size的百分比),值为NSNumber(float),默认为0,即不改变。正数只改变描边宽度,负数同时改变文字的描边和填充宽度。例如,对于常见的空心字,这个值通常为3.0。
    注意,必须与NSStrokeColorAttributeName一起使用才行,当这两个属性一起设置的时候,NSForegroundColorAttributeName将无效。

  • NSShadowAttributeName

    设置文字的硬性属性,值为NSShadow类型。类似于layer中得shadow

  • NSTextEffectAttributeName

    设置文本的特殊效果,现在只能设置为NSTextEffectLetterpressStyle

  • NSAttachmentAttributeName

    值为NSTextAttachment类型,上面已经有说明

  • NSLinkAttributeName

    值为一个NSURL,主要用在UITextView中,见上面的例子

  • NSBaselineOffsetAttributeName

    文字内容在垂直方向的偏移,用于将文字在垂直方向居中。其值为NSNumber(float)类型,正数为向上偏移。

  • NSUnderlineColorAttributeName

    下划线的颜色,需结合NSUnderlineStyleAttributeName一起使用

  • NSStrikethroughColorAttributeName

    删除线的颜色,需结合NSStrikethroughStyleAttributeName一起使用

  • NSObliquenessAttributeName

    设置字体的倾斜程度,值越大就倾斜的就越厉害,其值为NSNumber(float)类型,测试当值为1的时候刚好

  • NSExpansionAttributeName

    水平方向拉伸的程度,值为NSNumber(float)类型,测试0.5的时候刚好。

  • NSWritingDirectionAttributeName

    文本的书写方向,从左往右或者从右往左,值为下面的NSWritingDirectionFormatType枚举类型

  • NSVerticalGlyphFormAttributeName

    竖直方向排版类型

Values

NSUnderlineStyleAttributeNameNSStrikethroughStyleAttributeName 的值得类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// NSUnderlineStyleAttributeName and NSStrikethroughStyleAttributeName
typedef NS_ENUM(NSInteger, NSUnderlineStyle) {
NSUnderlineStyleNone = 0x00,
NSUnderlineStyleSingle = 0x01,
NSUnderlineStyleThick = 0x02,
NSUnderlineStyleDouble = 0x09,
NSUnderlinePatternSolid = 0x0000,
NSUnderlinePatternDot = 0x0100,
NSUnderlinePatternDash = 0x0200,
NSUnderlinePatternDashDot = 0x0300,
NSUnderlinePatternDashDotDot = 0x0400,
NSUnderlineByWord = 0x8000
}

NSWritingDirectionAttributeName的值得类型:

1
2
3
4
typedef NS_ENUM(NSInteger, NSWritingDirectionFormatType) {
NSWritingDirectionEmbedding = (0 << 1),
NSWritingDirectionOverride = (1 << 1)
}