前言
這幾天在項目中用到了一點圖文混排的内容。但是如果使用github上面那些大神們的架構有一點大材小用,因為關于圖文混排的内容就有那麼一點,是以自己仔細研究了下NSAttributedString這個類,雖然他和 NSString看着相同,但是用法還是有很大的差別。現特寫于此,一方面為了自己加深記憶,另一方面如果有對這方面知識點不了解的童鞋,可以參考一下我的了解。(本文具體是對NSAttributedString屬性,方法的描述和執行個體),這裡以OC為例進行的,如果需要Swift方面的,可以下載下傳Demo(Swift版)進行參考。
正文
我想了好久不知道怎麼開始這一塊,最後絕對跟着蘋果的API文檔順序走,這樣可能會好一點。我去看一個類的第一眼看的一定是這個類的構造方法,因為隻有存在了才有進行下一步的可能性。Demo下載下傳(iOS版) Demo下載下傳(Swift版)
NSAttributedString
構造方法:
//直接建立
- (instancetype)initWithString:(NSString *)str;
//建立并附帶屬性
- (instancetype)initWithString:(NSString *)str attributes:(nullable NSDictionary*)attrs;
//加載一條已經存在的
NSAttributedString- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr;
這三個構造方法隻要大家對nsstring熟悉,應該都會大緻了解他們的意思和使用的不同。代碼執行個體如下:
//構造方法
//原始資料
NSString *originStr = @"某農場爆發瘋牛病,一女記者聞訊趕到,采訪農場主:“請問這瘋牛病是從何而來的“你想想,每天都有人來捏你的XX,但是又不和你做ai,你會不會瘋掉“";
//第一種
NSAttributedString *string1 = [[NSAttributedString alloc] initWithString:originStr];textView.attributedText = string1;
//第二種
NSAttributedString *string2 = [[NSAttributedString alloc] initWithString:originStr attributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];textView.attributedText = string2;
//第三種
NSAttributedString *string3 =[[NSAttributedString alloc] initWithAttributedString:string2];textView.attributedText = string3;
用一個UITextView顯示内容,在這裡我們可以看出來,這幾種構造方法的不同之處。第一種是直接用字元串建立,第二種在你建立的時候可以直接給他一個屬性值,第三種是直接加載一個已經存在的NSAttributedString類型。
//判斷兩個NSAttributedString是否相等
-(BOOL)isEqualToAttributedString:(NSAttributedString *)other;
這裡所說的相等,不是内容相等就可以,必須所設定的内容的屬性也要相等才可以。代碼如下:
NSAttributedString *string4 = [[NSAttributedString alloc]initWithString:originStrattributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
//判斷兩個NSAttributedString是否相等
BOOL flag = [string1 isEqualToAttributedString:string2];
NSLog(@"flag === %i",flag);
BOOL flag2 = [string4 isEqualToAttributedString:string3];
NSLog(@"flag2 === %i,string4=%p,---string3=%p",flag2,string4,string3);
//NSObjec的方法
BOOL flag3 = [string4 isEqual:string3];
NSLog(@"flag3 === %i,string4=%p,---string3=%p",flag3,string4,string3);
在這裡建立是string4的目的就是看一下,這個方法判斷的相等與字元串的位址有沒有關系,結果顯示,沒有關系,隻要内容與屬性相等,傳回的結果就是yes。
//截取字元
-(NSAttributedString *)attributedSubstringFromRange:(NSRange)range;
這個方法的作用就是把本身的字元截取後賦予其他字元,代碼如下:
//截取
NSAttributedString *string5 = [string4 attributedSubstringFromRange:NSMakeRange(0, 10)];
在這裡就是把string4的從0到10的字元截取,賦予string5.
NSAttributedString的方法基本就是這些,還有幾個方法是關于内容的周遊,檢視的方法
- (nullable id)attribute:(NSString *)attrName atIndex:(NSUInteger)location effectiveRange:(nullable NSRangePointer)range;
- (NSDictionary*)attributesAtIndex:(NSUInteger)location longestEffectiveRange:(nullable NSRangePointer)range inRange:(NSRange)rangeLimit;
- (nullable id)attribute:(NSString *)attrName atIndex:(NSUInteger)location longestEffectiveRange:(nullable NSRangePointer)range inRange:(NSRange)rangeLimit;
- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (^)(NSDictionary*attrs, NSRange range, BOOL *stop))block NS_AVAILABLE(10_6, 4_0);
- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (^)
他們的作用在我看來最大的用處之一就是輸入表情顯示的時候去周遊檢視用的。這三個方法我沒有深究,現在我還沒有用到。
我們都知道,既然有一個不可變的NSAttributedString,一定會有一個對應的可變的這個類所對應的方法(蘋果一貫如此)。是以NSMutableAttributedString出現了,而且我們平時使用的時候應該NSMutableAttributedString用的是最多的,因為他可以做增,删,改這些工作,而這些在NSAttributedString中是不具備的。
NSMutableAttributedString
首先說構造方法,因為他是繼承自NSAttributedString的,是以他可以擁有NSAttributedString的所有的方法。(你再問我怎麼知道他們是繼承關系的??,請看下面官方API代碼)
@interface NSMutableAttributedString : NSAttributedString
建立一個可變的:
NSMutableAttributedString *muString1 = [[NSMutableAttributedString alloc] initWithString:originStr];
也可以用
//官方API
- (void)setAttributedString:(NSAttributedString *)attrString;
這個方法增加一個字元串。代碼如下:
[muString1 setAttributedString:string2];
增:
增加的方式有幾種,第一種直接追加
//官方API
- (void)appendAttributedString:(NSAttributedString *)attrString;
這個方法是在字元串的末尾直接追加字元(不能是普通的字元串,看API 方法後面的屬性提示),代碼如下:
//追加
[muString1 appendAttributedString:string1];
第二種是插入
- (void)insertAttributedString:(NSAttributedString *)attrString atIndex:(NSUInteger)loc;
這個方法就是在原來内容中的某個地方插入一段字元,代碼如下:
//插入内容(富文本)
[muString1 insertAttributedString:string5 atIndex:muString1.length];
删:
删除字元就有一個方法:
//API
- (void)deleteCharactersInRange:(NSRange)range;
代碼如下:
//删除内容
[muString1 deleteCharactersInRange:NSMakeRange(0, 200)];
改:
我們來看一下,NSMutableAttributedString的改這個特性系統
API
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString;
這兩個方法的的差別就是一個使用普通字元串替換原來的字元,一個使用NSAttributedString類型的字元串替換原來的字元。代碼如下:
NSMutableAttributedString *muString1 = [[NSMutableAttributedString alloc] initWithString:originStr];
//内容代替
[muString1 replaceCharactersInRange:NSMakeRange(0, 10) withString:@"hello,world"];
//替代
[muString1 replaceCharactersInRange:NSMakeRange(0, 10) withAttributedString:string1];
接下來我們要進入使用NSMutableAttributedString的最重要的内容了,給我們的字元串内容增加各種顯示屬性:顔色,大小,下劃線……。
設定屬性的方法:
- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range;- (void)addAttributes:(NSDictionary*)attrs range:(NSRange)range;
這兩個方法感覺就是NSMutableAttributedString的精華所在,是他與普通的字元串的最大的差別之處之一(還有圖文混排這個差別)。
我們可以這樣給某段字元串一個大小:
//字型大小
[muString1 addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:35] range:NSMakeRange(0, muString1.length)];
我們也可以這樣給某段字元串一個字一顔色:
//字型顔色
[muString1 addAttribute:NSForegroundColorAttributeName value:[UIColor lightGrayColor] range:NSMakeRange(0, 20)];
我們又可以這樣給某段字元串一個背景顔色:
//字型背景色
[muString1 addAttribute:NSBackgroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(20, 10)];
我們可以給某段字元串一個下劃線顯示:
//下劃線
[muString1 addAttribute:NSUnderlineStyleAttributeName value:@(1) range:NSMakeRange(0, 20)];
感覺下劃線顔色不好看,好的,來換個下劃線顔色:
//下劃線顔色
[muString1 addAttribute:NSUnderlineColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, 10)];
給某段字元串删除線顯示(個人感覺在電商app裡面,原價那個屬性最适合這個):
//删除線
[muString1 addAttribute:NSStrikethroughStyleAttributeName value:@(1) range:NSMakeRange(40, 10)];
來點進階的,做個邊線(就是字型中空的樣式):
//邊線寬度
[muString1 addAttribute:NSStrokeWidthAttributeName value:@(1) range:NSMakeRange(10, 30)];
給邊線來個顔色(必須有邊線寬度才會有效果):
//邊線顔色,必須有邊線寬度
[muString1 addAttribute:NSStrokeColorAttributeName value:[UIColor blueColor] range:NSMakeRange(10, 10)];
最後,來個陰影效果:
//陰影
NSShadow *shadow = [[NSShadow alloc]init];
shadow.shadowColor = [UIColor purpleColor];
shadow.shadowOffset = CGSizeMake(0, 2);
[muString1 addAttribute:NSShadowAttributeName value:shadow range:NSMakeRange(0, 50)];
好了,示範的就這麼多吧,還有好多屬性,童鞋自己去查一下就行了。
上面示範的都是加載一個屬性的方法,我們也可以以字典的方式一次同時加載多個屬性。代碼如下:
[muString1 addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:20],NSBackgroundColorAttributeName:[UIColor yellowColor]} range:NSMakeRange(0, muString1.length)];
有一個需要注意,排版這個屬性NSVerticalGlyphFormAttributeName。我用了一下但是顯示沒有反應,上網查了下,原來在ios上這個屬性隻顯示預設的橫版。
如果有的屬性你不想用了,又不知道在哪寫的了,那麼接下來這個方法正好是你需要的:
- (void)removeAttribute:(NSString *)name range:(NSRange)range;
移除對應的屬性,代碼如下:
//移除屬性
[muString1 removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, 20)];
NSParagraphStyleAttributeName 這個屬性我上網查了好久,但是每次使用都是失敗,說是段落屬性,看了api,也就是兩個構造,幾個屬性,但是就是用不對……。
圖文混排
首先說一下思路就是把圖檔插入或者追加到NSMutableAttributedString字元串裡面。但是我們知道NSMutableAttributedString隻能插入或者追加本類型的内容,那麼我們怎麼把圖檔插入呢,這裡我們就要用到NSTextAttachment這個類。
NSTextAttachment這個類裡面隻有幾個方法和屬性,大家可以去看一下,這裡不再多說。
插入圖檔代碼如下:
//插入圖檔,與字型大小不一樣
NSTextAttachment *attchment = [[NSTextAttachment alloc]init];
attchment.image = [UIImage imageNamed:@"share.jpg"];
NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:attchment];
[muString1 insertAttributedString:imageString atIndex:20];
用完這個方法Run一下你會發現圖檔的大小顯示是以真實的大小顯示的,那麼我們怎麼讓他适應字型大小呢,我現在有兩種辦法。
一,自己去改變圖檔大小,代碼如下:
NSTextAttachment *attchment = [[NSTextAttachment alloc]init];
UIImage *icon = [UIImage imageNamed:@"share.jpg"];
attchment.image = [self imageWithImage:icon scaleToSize:CGSizeMake(20, 20)];
NSAttributedString *imageString2 = [NSAttributedString attributedStringWithAttachment:attchment];
[muString1 insertAttributedString:imageString2 atIndex:20];
//修改圖檔大小
- (UIImage *)imageWithImage:(UIImage *)image scaleToSize:(CGSize)size
{
UIImage *newImage = nil;
UIGraphicsBeginImageContextWithOptions(size, false, 0.0);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
這種方法是自己修改圖檔的大小去适應字型大小,比較麻煩。還有一種方法,自己繼承NSTextAttachment這個類,去實作一個NSTextAttachment的方法,代碼如下:
@implementation SYFTextAttachment
//讓加載的内容與對應的富文本大小對應
-(CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex
{
return CGRectMake(0, 0, lineFrag.size.height, lineFrag.size.height);
}
在使用的時候導入你自己寫的這個繼承類,然後寫入對應的代碼:
//插入圖檔,與字型大小一樣
SYFTextAttachment *syfattchment = [[SYFTextAttachment alloc]init];
syfattchment.image = [UIImage imageNamed:@"share.jpg"];
NSAttributedString *imageString3 = [NSAttributedString attributedStringWithAttachment:syfattchment];
[muString1 insertAttributedString:imageString3 atIndex:60];
結語
本篇結束,我隻是一個小菜鳥,寫下來的最大的作用怕自己以後忘了。裡面有不對的地方,希望指出,我立馬改正。最後祝大家新年快樂!!!