《Text Kit学习(入门和进阶).pdf》由会员分享,可在线阅读,更多相关《Text Kit学习(入门和进阶).pdf(17页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。
1、Text Kit学习(入门和进阶)转自 TracyYih的博客 更详细的内容可以参考官方文档Text Programming Guide for iOS。Text Kit指的是 UIKit框架中用于提供高质量排版服务的一些类和协议,它让程序能够存储,排版和显示文本信息,并“”阅读器Text Kit转自 TracyYih的博客更详细的内容可以参考官方文档 Text Programming Guidefor iOS。“Text Kit指的是 UIKit 框架中用于提供高质量排版服务的一些类和协议,它让程序能够存储,排版和显示文本信息,并支持排版所需要的所有特性,包括字距调整、连写、换行和对齐等。”
2、以前,如果我们想实现复杂的文本排版,例如在textView 中显示不同样式的文本,或者图片和文字混排,你可能就需要借助于 UIWebView或者深入研究一下 Core Text。在iOS6 中,UILabel、UITextField、UITextView增加了一个NSAttributedString 属性,可以稍微解决一些排版问题,但是支持的力度还不够。现在 Text Kit完全改变了这种现状。Text Kit是基于 Core Text构建的快速、先进的文本排版和渲染引擎,并且与 UIKit 很好的集合。UITextView,UITextField、UILabel 都已经基于 Text Kit
3、重新构建,所以它们都支持分页文本、文本包装、富文本编辑、交互式文本着色、文本折叠和自定义截取等特性。所有这些 UI 控件现在都以同样的方式构建,在它们后面,一个 NSTextStorage对象保存着文本的主要信息,它本身是 NSMutableAttributedString 的子类,支持分批编辑。这就意味着你可以改变一个范围内的字符的样式而不用整体替换文本内容。self.textView.textStorage beginEditing;selfmarkWord:Alice inTextStorage:self.textView.textStorage;self.textView.textSt
4、orage endEditing;Text storage管理者一系列的 NSLayoutManager 对象,当它的字符或者属性改变时会通知到自己所管理的 layout Manager对象以便它们作出相应的反应。在 layout manager 上面是一个 NSTextContainer对象,用于为 layout manager 定义坐标系和一些几何特性。例如,如果你想 UITextView中的文本环绕在一张图片四周,你可以给 text container 设定一个排除路径(exclusion path)。UIBezierPath*exclusion=ButterflyBezierPath;
5、self.textView.textContainer.exclusionPaths=exclusion;Text container能够处理击中测试(hit tests),所以可以定位到点击的字符在文本中的位置。此外它还提供一些代理方法让开发者能够自己定义链接点击后的处理事件。通过基于 Text Kit重新构建 UILabel、UITextField和UITextView,苹果给开发者更大的灵活性和能力来设计富文本视图,同时简化了这些控件的使用,因为它们是以同样的方式设计的,所有这些好处都是站在巨人(Core Text)的肩上。通常更强大的功能和灵活性也就意味着需要更多的设置和管理,但是,如
6、果你只是想显示一段简单的文本,你还是可以像以前一样使用。self.textLabel.text=Hello Text Kit;本文翻译自iOS 7:Text KitText Kit进阶上一篇文章 Text Kit入门中我们主要了解了什么是 Text Kit及它的一些架构和基本特性,这篇文章中会涉及关于Text Kit的更多具体应用。Text Kit是建立在 Core Text框架上的,我们知道CoreText.framework是一个庞大而复杂的框架,而 Text Kit在继承了 Core Text强大功能的同时给开发者提供了比较友好的面向对象的 API。本文主要介绍 Text Kit下面四个
7、特性:动态字体(Dynamic type)凸版印刷体效果(Letterpress effects)路径排除(Exclusion paths)动态文本格式化和存储(Dynamic text formatting and storage)动态字体(Dynamic type)动态字体是 iOS7 中新增加的比较重要的特性之一,程序应该按照用户设定的字体大小和粗细来显示文本内容。分别在设置通用辅助功能和设置通用文字大小中可以设置文本在应用程序中显示的粗细和大小。iOS7 对系统字体在显示上做了一些优化,让不同大小的字体在屏幕上都能清晰的显示。通常用户设置了自己偏好的字体,他们希望在所有程序中都看到文本
8、显示是根据他们的设定进行调整。为了实现这个,开发者需要在自己的应用中给文本控件设置当前用户设置字体,而不是指定死字体及大小。可以通过 UIFont 中新增的 preferredFontForTextStyle:方法来获取用户偏好的字体。iOS7 中给出了 6 中字体样式供选择:UIFontTextStyleHeadlineUIFontTextStyleBodyUIFontTextStyleSubheadlineUIFontTextStyleFootnoteUIFontTextStyleCaption1UIFontTextStyleCaption2为了让我们的程序支持动态字体,需要按一下方式给文
9、本控件(通常是指UILabel,UITextField,UITextView)设定字体:self.textView.font=UIFontpreferredFontForTextStyle:UIFontTextStyleBody;这样设置之后,文本控件就会以用户设定的字体大小及粗细显示,但是如果程序在运行时,用户切换到设置里修改了字体,这是在切回程序,字体并不会自动跟着变。这时就需要我们自己来更新一下控件的字体了。在系统字体修改时,系统会给运行中的程序发送UIContentSizeCategoryDidChangeNotification 通知,我们只需要监听这个通知,并重新设置一下字体即可。
10、NSNotificationCenter defaultCenter addObserver:selfselector:selector(preferredContentSizeChanged:)name:UIContentSizeCategoryDidChangeNotificationobject:nil;-(void)preferredContentSizeChanged:(NSNotification*)notificationself.textView.font=UIFontpreferredFontForTextStyle:UIFontTextStyleBody;当然,有的时候要适
11、应动态修改的字体并不是这么设置一下就完事了,控件的大小可能也需要进行相应的调整,这时我们程序中的控件大小也不应该写死,而是需要根据字体大小来计算.凸版印刷体效果(Letterpress effects)凸版印刷替效果是给文字加上奇妙阴影和高光,让文字看起有凹凸感,像是被压在屏幕上。当然这种看起来很高端大气上档次的效果实现起来确实相当的简单,只需要给AttributedString 加一个 NSTextEffectAttributeName 属性,并指定该属性值为 NSTextEffectLetterpressStyle就可以了。tionary*attributes=NSForegroundCo
12、lorAttributeName:UIColorredColor,NSFontAttributeName:UIFontpreferredFontForTextStyle:UIFontTextStyleHeadline,NSTextEffectAttributeName:NSTextEffectLetterpressStyle;self.titleLabel.attributedText=NSAttributedString allocinitWithString:Title attributes:attributes;在 iOS7 系统自带的备忘录应用中,苹果就使用了这种凸版印刷体效果。路径排
13、除(Exclusion paths)在排版中,图文混排是非常常见的需求,但有时候我们的图片并一定都是正常的矩形,这个时候我们如果需要将文本环绕在图片周围,就可以用路径排除(exclusion paths)了。Explosion pats 基本原理是将需要被文本留出来的形状的路径告诉文本控件的 NSTextContainer对象,NSTextContainer在文字排版时就会避开该路径。UIBezierPath*floatingPath=self pathOfImage;self.textView.textContainer.exclusionPaths=floatingPath;所以实现 Ex
14、clusion paths 的主要工作就是获取这个 path。动态文本格式化和存储(Dynamic text formatting and storage)好了,到现在我们知道了 Text Kit可以动态的根据用户设置的字体大小进行调整,但是如果具体某个文本显示控件中的文本样式能够动态调整是不是会更酷一些呢?实现这些才是真正体现 Text Kit强大之处的时候,在此之前你需要理解 Text Kit中的文本存储系统是怎么工作的,下图显示了 Text Kit中文本的保存、渲染和现实之间的关系。当你使用 UITextView、UILabel、UITextField控件的时候,系统会自动创建上面这些类
15、,你可以选择直接使用这么默认的实现或者为你的控件自定义这几个中的任何一个。1.NSTextStorage本身继承与 NSMutableAttributedString,它是以 attributed string 的形式保存需要渲染的文本,并在文本内容改变的时候通知到对应的 layout manager 对象。通常你需要创建 NSTextStorage的子类来在文本改变时进行文本显示样式的更新。2.NSLayoutManager 作为文本控件中的排版引擎接收保存的文本并在屏幕上渲染出来。3.NSTextContainer描述了文本在屏幕上显示时的几何区域,每个 text container 与一
16、个具体的 UITextView相关联。如果你需要定义一个很复杂形状的区域来显示文本,你可能需要创建 NSTextContainer子类。要实现我们上面描述的动态文本格式化功能,我们需要创建NSTextStorage子类以便在用户输入文本的时候动态的增加文本属性。自定义了 text storage 后,我们需要替换调UITextView默认的 text storage。创建 NSTextStorage的子类我们创建 NSTextStorage子类,命名为 MarkupTextStorage,在实现文件中添加一个成员变量:#import MarkupTextStorage.himplementat
17、ionMarkupTextStorage NSMutableAttributedString*_backingStore;-(id)init self=super init;if(self)_backingStore=NSMutableAttributedString alloc init;returnself;endNSTextStorage的子类需要重载一些方法提供NSMutableAttributedString 类型的 backing store 信息,所以我们继续添加下面代码:-(NSString*)string return _backingStore string;-(NSDic
18、tionary*)attributesAtIndex:(NSUInteger)locationeffectiveRange:(NSRangePointer)range return_backingStore attributesAtIndex:locationeffectiveRange:range;-(void)replaceCharactersInRange:(NSRange)rangewithString:(NSString*)str self beginEditing;_backingStore replaceCharactersInRange:range withString:str
19、;self edited:NSTextStorageEditedCharacters|NSTextStorageEditedAttributesrange:rangechangeInLength:str.length-range.length;selfendEditing;-(void)setAttributes:(NSDictionary*)attrsrange:(NSRange)range self beginEditing;_backingStore setAttributes:attrs range:range;selfedited:NSTextStorageEditedAttribu
20、tesrange:range changeInLength:0;self endEditing;后面两个方法都是代理到 backing store,然后需要被beginEditing edited endEditing 包围,而且必须在文本编辑时按顺序调用来通知 text storage 对应的 layout manager。你可能发现子类化 NSTextStorage需要写不少的代码,因为NSTextStorage是一个类集群中的一个开发接口,不能只是继承它然后重载很少的方法来拓展它的功能,而是需要自己实现很多细节。类集群(Class cluster)是苹果 Cocoa(Touch)框架中常
21、用的设计模式之一。类集群是 Objective-C 中对抽象工厂模式的简单实现,为创建一些列相关或独立对象提供了统一的接口而不用指定具体的类。常用的像 NSArray 和 NSNumber 事实上也是一系列类集群的开放接口。苹果使用类集群是为了将一些类具体类隐藏在开放的抽象父类之下,外面通过抽象父类的方法来创建私有子类的实例,并且外界也完全不知道工厂分配到了哪个私有类,因为它们始终只和开放接口交互。使用类集群确实简化了接口,让类更容易被使用,但是要知道鱼和熊掌不可兼得,你又想简单又想可拓展性强,哪有那么好的事啊?所以创建一个类集群中的抽象父类就没有那么简单了。好了,上面解释了这么多其实主要就说
22、明了为什么子类化NSTextStorage需要写这么多代码,下面要在 UITextView使用我们自定义的 text storage 了。设置 UITextView-(void)createMarkupTextView NSDictionary*attributes=NSFontAttributeName:UIFontpreferredFontForTextStyle:UIFontTextStyleBody;NSString*content=NSStringstringWithContentsOfFile:NSBundle mainBundlepathForResource:content o
23、fType:txtencoding:NSUTF8StringEncodingerror:nil;NSAttributedString*attributedString=NSAttributedString alloc initWithString:contentattributes:attributes;_textStorage=MarkupTextStoragealloc init;_textStoragesetAttributedString:attributedString;CGRecttextViewRect=CGRectMake(20,60,280,self.view.bounds.
24、size.height-100);NSLayoutManager*layoutManager=NSLayoutManageralloc init;NSTextContainer*textContainer=NSTextContainer allocinitWithSize:CGSizeMake(textViewRect.size.width,CGFLOAT_MAX);layoutManageraddTextContainer:textContainer;_textStorageaddLayoutManager:layoutManager;_textView=UITextView alloc i
25、nitWithFrame:textViewRecttextContainer:textContainer;_textView.delegate=self;self.view addSubview:_textView;很长的代码,下面我们来看看都做了些啥:1.创建了一个自定义的 text storage 对象,并通过 attributedstring 保存了需要显示的内容;2.创建了一个 layout manager 对象;3.创建了一个 text container 对象并将它与 layout manager 关联,然后该 text container 再和 text storage 对象关联
26、;4.通过 text container 创建了一个 text view 并显示。你可以将代码和前面那对象间的关系图对应着理解一下。动态格式化继续在 MarkupTextStorage.m文件中添加如下方法:-(void)processEditing selfperformReplacementsForRange:self editedRange;super processEditing;processEditing 在 layout manager 中文本修改时发送通知,它通常也是处理一些文本修改逻辑的好地方。继续添加:-(void)performReplacementsForRange:(
27、NSRange)changedRangeNSRange extendedRange=NSUnionRange(changedRange,_backingStore stringlineRangeForRange:NSMakeRange(changedRange.location,0);extendedRange=NSUnionRange(changedRange,_backingStore stringlineRangeForRange:NSMakeRange(NSMaxRange(changedRange),0);self applyStylesToRange:extendedRange;这
28、个方法用于扩大文本匹配的范围,因为 changedRange 只是标识出一个字符,lineRangeForRange 会将范围扩大到当前的一整行。下面就剩下匹配特定格式的文本来显示对应的样式了:-(NSDictionary*)createAttributesForFontStyle:(NSString*)stylewithTrait:(uint32_t)trait UIFontDescriptor*fontDescriptor=UIFontDescriptorpreferredFontDescriptorWithTextStyle:UIFontTextStyleBody;UIFontDescr
29、iptor*descriptorWithTrait=fontDescriptorfontDescriptorWithSymbolicTraits:trait;UIFont*font=UIFont fontWithDescriptor:descriptorWithTrait size:0.0;return NSFontAttributeName:font;-(void)createMarkupStyledPatternsUIFontDescriptor*scriptFontDescriptor=UIFontDescriptorfontDescriptorWithFontAttributes:UI
30、FontDescriptorFamilyAttribute:Bradley Hand;/1.base our script font on the preferred body font sizeUIFontDescriptor*bodyFontDescriptor=UIFontDescriptorpreferredFontDescriptorWithTextStyle:UIFontTextStyleBody;NSNumber*bodyFontSize=bodyFontDescriptor.fontAttributesUIFontDescriptorSizeAttribute;UIFont*s
31、criptFont=UIFontfontWithDescriptor:scriptFontDescriptor size:bodyFontSizefloatValue;/2.create the attributesNSDictionary*boldAttributes=selfcreateAttributesForFontStyle:UIFontTextStyleBodywithTrait:UIFontDescriptorTraitBold;NSDictionary*italicAttributes=selfcreateAttributesForFontStyle:UIFontTextSty
32、leBodywithTrait:UIFontDescriptorTraitItalic;NSDictionary*strikeThroughAttributes=NSStrikethroughStyleAttributeName:1,NSForegroundColorAttributeName:UIColor redColor;NSDictionary*scriptAttributes=NSFontAttributeName:scriptFont,NSForegroundColorAttributeName:UIColorblueColor;NSDictionary*redTextAttrib
33、utes=NSForegroundColorAttributeName:UIColor redColor;_replacements=(*w+(sw+)*):boldAttributes,(_w+(sw+)*_):italicAttributes,(w+(sw+)*):strikeThroughAttributes,(w+(sw+)*):scriptAttributes,s(A-Z2,)s:redTextAttributes;-(void)applyStylesToRange:(NSRange)searchRangeNSDictionary*normalAttrs=NSFontAttribut
34、eName:UIFont preferredFontForTextStyle:UIFontTextStyleBody;/iterate over each replacementfor(NSString*key in_replacements)NSRegularExpression*regex=NSRegularExpressionregularExpressionWithPattern:keyoptions:0error:nil;NSDictionary*attributes=_replacementskey;regexenumerateMatchesInString:_backingSto
35、re stringoptions:0range:searchRangeusingBlock:(NSTextCheckingResult*match,NSMatchingFlags flags,BOOL*stop)/applythe styleNSRangematchRange=match rangeAtIndex:1;self addAttributes:attributes range:matchRange;/reset the style to the originalif(NSMaxRange(matchRange)+1<self.length)selfaddAttributes:normalAttrsrange:NSMakeRange(NSMaxRange(matchRange)+1,1);在 createMarkupStyledPatterns 初始化方法中调用createMarkupStyledPatterns,通过正则表达式来给特定格式的字符串设定特定显示样式,形成一个对应的字典。然后在applyStylesToRange:中利用已定义好的样式字典来给匹配的文本端增加样式。到这里本篇文章的内容就结束了,其实前面三点都很简单,稍微过一下就能用。最后一个动态文本格式化内容稍微多一点,可以结合我的代码 TextKitDemo来看。参考链接:http:/
限制150内