图文混排

首先是NSTextContainer:

The NSTextContainer class defines a region in which text is laid out.

An NSTextContainer object defines rectangular regions, and you can define exclusion paths inside the textcontainer'sboundingrectanglesothattextflowsaroundtheexclusionpathasitislaidout.

接着是NSLayoutManager:

An NSLayoutManager object coordinates the layout and display of characters held in an NSTextStorage object. It maps Unicode character codes to glyphs, sets the glyphs in a series of NSTextContainer objects, and displays them in a series of text view objects.

最后是NSTextStorage:

NSTextStorage is a semiconcrete subclass of NSMutableAttributedString that manages a set of client NSLayoutManagerobjects,notifyingthemofanychangestoitscharactersorattributessothattheycanrelay and redisplay the text as needed.


按照个人理解:

NSTextStorage保存并管理UITextView要展示的文字内容,该类是NSMutableAttributedString的子类,由于可以灵活地往文字添加或修改属性,所以非常适用于保存并修改文字属性。

NSLayoutManager用于管理NSTextStorage其中的文字内容的排版布局。

NSTextContainer则定义了一个矩形区域用于存放已经进行了排版并设置好属性的文字。

以上三者是相互包含相互作用的层次关系。


接下来是三种类的使用:

[cpp]  view plain copy
  1. CGRect textViewRect = CGRectInset(self.view.bounds, 10.0, 20.0);  
  2.   
  3. // NSTextContainer  
  4. NSTextContainer *container = [[NSTextContainer alloc] initWithSize:CGSizeMake(textViewRect.size.width, CGFLOAT_MAX)]; // new in iOS 7.0  
  5. container.widthTracksTextView = YES; // Controls whether the receiveradjusts the width of its bounding rectangle when its text view is resized  
  6.   
  7.   
  8. // NSLayoutManager  
  9. NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; // new in iOS 7.0  
  10. [layoutManager addTextContainer:container];  
  11.   
  12.   
  13. // NSTextStorage subclass  
  14. self.textStorage = [[TextStorage alloc] init]; // new in iOS 7.0  
  15. [self.textStorage addLayoutManager:layoutManager];  
首先是初始化类对象,然后通过add方法来建立三者之间的关系。

最后必须注意要在UITextView中通过initWithFrame:textContainer:方法来添加相应的NSTextContainer从而设置好对应的文字。

[cpp]  view plain copy
  1. // UITextView  
  2. UITextView *newTextView = [[UITextView alloc] initWithFrame:textViewRect textContainer:container];  
  3. newTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;  
  4. newTextView.scrollEnabled = YES;  
  5. newTextView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;  
  6. // newTextView.editable = NO;  
  7. newTextView.font = [UIFont fontWithName:self.textStorage.fontName size:18.0];  
  8. newTextView.dataDetectorTypes = UIDataDetectorTypeAll;  
  9. self.textView = newTextView;  
  10. [self.view addSubview:self.textView];  


如果要使用NSTextStorage类来改变文字的属性,分别使用

[cpp]  view plain copy
  1. [_textStorage beginEditing];  
[cpp]  view plain copy
  1. [_textStorage endEditing];  
来向UITextStorage类或其子类发送开始或完成文字编辑的消息。在两条语句之间进行相应的文字编辑,例如为文字添加letterpress style:

[cpp]  view plain copy
  1. [_textStorage beginEditing];  
  2. NSDictionary *attrsDic = @{NSTextEffectAttributeName: NSTextEffectLetterpressStyle};  
  3. UIKIT_EXTERN NSString *const NSTextEffectAttributeName NS_AVAILABLE_IOS(7_0);          // NSString, default nil: no text effect  
  4. NSMutableAttributedString *mutableAttrString = [[NSMutableAttributedString alloc] initWithString:@"Letterpress" attributes:attrsDic];  
  5. NSAttributedString *appendAttrString = [[NSAttributedString alloc] initWithString:@" Append:Letterpress"];  
  6. [mutableAttrString appendAttributedString:appendAttrString];  
  7. [_textStorage setAttributedString:mutableAttrString];  
  8. [_textStorage endEditing];  
可以看到通过attribute来改变文字的属性是非常简单的。


又如通过NSTextStorage类为文字添加颜色属性:

[cpp]  view plain copy
  1. [_textStorage beginEditing];  
  2. /* Dynamic Coloring Text */  
  3. self.textStorage.bookItem = [[BookItem alloc] initWithBookName:@"Dynamic Coloring.rtf"];  
  4. self.textStorage.tokens = @{@"Alice": @{NSForegroundColorAttributeName: [UIColor redColor]},  
  5.                             @"Rabbit": @{NSForegroundColorAttributeName: [UIColor greenColor]},  
  6.                             DefaultTokenName: @{NSForegroundColorAttributeName: [UIColor blackColor]}  
  7.                             };  
  8. [_textStorage setAttributedString:_textStorage.bookItem.content];  
  9. [_textStorage endEditing];  
其处理过程要看看重写的NSTextStorage子类:

接口部分:

[cpp]  view plain copy
  1. NSString *const DefaultTokenName;  
  2.   
  3. @interface TextStorage : NSTextStorage  
  4. @property (nonatomic, strong) NSString     *fontName;  
  5. @property (nonatomic, copy)   NSDictionary *tokens; // a dictionary, keyed by text snippets(小片段), with attributes we want to add  
  6. @property (nonatomic, strong) BookItem     *bookItem;  
  7. @end  
以及.m中的匿名类别:

[cpp]  view plain copy
  1. #import "TextStorage.h"  
  2.   
  3. NSString *const DefaultTokenName = @"DefaultTokenName";  
  4.   
  5. @interface TextStorage ()  
  6. {  
  7.     NSMutableAttributedString *_storingText; // 存储的文字  
  8.     BOOL _dynamicTextNeedsUpdate;            // 文字是否需要更新  
  9. }  
  10. @end  
然后是基本的初始化方法:

[cpp]  view plain copy
  1. // get fontName Snell RoundHand  
  2. -(NSString *)fontName  
  3. {  
  4.     NSArray *fontFamily = [UIFont familyNames];  
  5.     NSString *str = fontFamily[2];  
  6.     // NSLog(@"%@", str);  
  7.     return str;  
  8. }  
  9.   
  10. // initial  
  11. -(id)init  
  12. {  
  13.     self = [super init];  
  14.     if (self) {  
  15.         _storingText = [[NSMutableAttributedString alloc] init];  
  16.     }  
  17.     return self;  
  18. }  
重点来了,重写NSTextStorage类的子类必须重载以下四个方法:

[cpp]  view plain copy
  1. // Must override NSAttributedString primitive method  
  2. -(NSString *)string // 返回保存的文字  
  3. -(NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range // 获取指定范围内的文字属性  
  4.   
  5. // Must override NSMutableAttributedString primitive method  
  6. -(void)setAttributes:(NSDictionary *)attrs range:(NSRange)range           // 设置指定范围内的文字属性  
  7. -(void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str // 修改指定范围内的文字  


具体实现如下:

[cpp]  view plain copy
  1. // Must override NSAttributedString primitive method  
  2. // 返回保存的文字  
  3. -(NSString *)string  
  4. {  
  5.     return [_storingText string];  
  6. }  
  7.   
  8. // 获取指定范围内的文字属性  
  9. -(NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range  
  10. {  
  11.     return [_storingText attributesAtIndex:location effectiveRange:range];  
  12. }  
_storingText保存了NSTextStorage中的文字,string方法直接返回该变量的string值。

要获取文字属性时也可以直接从_storingText入手。


[cpp]  view plain copy
  1. // Must override NSMutableAttributedString primitive method  
  2. // 设置指定范围内的文字属性  
  3. -(void)setAttributes:(NSDictionary *)attrs range:(NSRange)range  
  4. {  
  5.     [self beginEditing];  
  6.     [_storingText setAttributes:attrs range:range];  
  7.     [self edited:NSTextStorageEditedAttributes range:range changeInLength:0]; // Notifies and records a recent change.  If there are no outstanding -beginEditing calls, this method calls -processEditing to trigger post-editing processes.  This method has to be called by the primitives after changes are made if subclassed and overridden.  editedRange is the range in the original string (before the edit).  
  8.     [self endEditing];  
  9. }  
  10.   
  11. // 修改指定范围内的文字  
  12. -(void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str  
  13. {  
  14.     [self beginEditing];  
  15.     [_storingText replaceCharactersInRange:range withString:str];  
  16.     [self edited:NSTextStorageEditedAttributes | NSTextStorageEditedCharacters range:range changeInLength:str.length - range.length];  
  17.     _dynamicTextNeedsUpdate = YES;  
  18.     [self endEditing];  
  19. }  
可以看到在设置或要改变文字的属性时必须分别调用beginEditing和endEditing方法,在两者之间进行相应的动作。


如果NSTextStorge类收到endEditing的通知,则调用processEditing方法进行处理。

[cpp]  view plain copy
  1. // Sends out -textStorage:willProcessEditing, fixes the attributes, sends out -textStorage:didProcessEditing, and notifies the layout managers of change with the -processEditingForTextStorage:edited:range:changeInLength:invalidatedRange: method.  Invoked from -edited:range:changeInLength: or -endEditing.  
  2. -(void)processEditing  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值