在iOS開發中,圖文混排一直都是UI編程的一個核心點,也有許多優秀的第三方引擎,其中頗有名的一套圖文混排的框架叫作DTCoreText。可是在前些日的作的一個項目中,我並無採用這套框架,緣由有二,一是這套框架體積很是大,而項目的需求其實並不過高;二是要在這套框架中修改一些東西,難度也很是大,我最終採用的是一個叫作RCLabel的第三方控件,通過一些簡單的優化和完善,達到了項目的要求。html
先來介紹一下我項目中的圖文混排的需求:首先我從服務器中取到的數據是字符串,可是其中穿插圖片的位置是一個HTML的圖片標籤,標籤裏的資源路徑就是圖片的請求地址。須要達到的要求是這些數據顯示出來後,圖片的位置要空出來,而後經過異步的網絡請求獲取圖片的數據,再將圖片插入文字中。編程
要本身實現一套這樣的引擎確實會比較麻煩,幸運的是RCLabel能夠完美的幫咱們解析帶有HTML標籤的數據,進行圖文混排,咱們先來看一下這個東西怎麼用,下面是我封裝的一個展現html數據的view:緩存
@interface YHBaseHtmlView()<YHRTLabelImageDelegate> { //RCLabel對象 RCLabel * _rcLabel; //保存屬性 用於異步加載完成後刷新 RTLabelComponentsStructure * _origenComponent; //含html標籤的數據字符串 NSString * _srt; } @end @implementation YHBaseHtmlView /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { //將rclabel初始化 _rcLabel = [[RCLabel alloc]init]; [self addSubview:_rcLabel]; } return self; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { _rcLabel = [[RCLabel alloc]initWithFrame:frame]; [self addSubview:_rcLabel]; } return self; } -(void)reSetHtmlStr:(NSString *)htmlStr{ _srt = htmlStr; //這個代理是我額外添加的 後面解釋 _rcLabel.imageDelegate=self; //設置frame _rcLabel.frame=CGRectMake(0, 0, self.frame.size.width, 0); //設置屬性 _origenComponent = [RCLabel extractTextStyle:htmlStr IsLocation:NO withRCLabel:_rcLabel]; _rcLabel.componentsAndPlainText = _origenComponent; //獲取排版後的size CGSize size = [_rcLabel optimumSize]; //從新設置frame _rcLabel.frame=CGRectMake(0, 0, _rcLabel.frame.size.width, size.height); self.frame=CGRectMake(self.frame.origin.x, self.frame.origin.y, _rcLabel.frame.size.width, size.height); } //這是我額外添加的代理方法的實現 -(void)YHRTLabelImageSuccess:(RCLabel *)label{ _origenComponent = [RCLabel extractTextStyle:_srt IsLocation:NO withRCLabel:_rcLabel]; _rcLabel.componentsAndPlainText = _origenComponent; CGSize size = [_rcLabel optimumSize]; _rcLabel.frame=CGRectMake(0, 0, _rcLabel.frame.size.width, size.height); self.frame=_rcLabel.frame; if ([self.delegate respondsToSelector:@selector(YHBaseHtmlView:SizeChanged:)]) { [self.delegate YHBaseHtmlView:self SizeChanged:self.frame.size]; } }
RCLabel的用法很簡單,總結來講只有三步:服務器
1.初始化並設置frame網絡
2.經過帶html標籤的數據進行屬性的初始化框架
3.將屬性進行set設置並重設視圖frame異步
RCLabel是很強大,而且代碼很簡練,可是其中處理圖片的部分必須是本地的圖片,即圖片html標籤中的路徑必須是本地圖片的名字,其內部是經過[UIImage ImageNamed:]這個方法進行圖片的渲染的,因此要達到咱們的須要,咱們須要對其進行一些簡單的擴展:async
一、在屬性設置方法中添加一個參數,來區分本地圖片與網絡圖片:ide
//我在這個方法中添加了location這個bool值,實際上rclabel這個參數也是我添加的,是爲了後面代理使用的 + (RTLabelComponentsStructure*)extractTextStyle:(NSString*)dataimage IsLocation:(BOOL)location withRCLabel:(RCLabel *)rcLabel;
二、在實現方法中添加以下代碼,由於原文件有1900多行,在其中弄清楚邏輯關係也確實費了我不小的力氣,我這裏只將我添加的代碼貼過來優化
#warning 這裏進行了兼容性處理 if (location) { //本地圖片的渲染 if (tempURL) { UIImage *tempImg = [UIImage imageNamed:tempURL]; component.img = tempImg; } }else{//這裏作遠程圖片數據的處理 //這裏我進行了緩存的操做,這個緩存中心是我封裝的框架中的另外一套東西,這裏能夠不用在乎 //先讀緩存 NSData * ceche = [[YHBaseCecheCenter sharedTheSingletion] readCecheFile:tempURL fromPath:YHBaseCecheImage]; if (ceche) { UIImage * tempImg = [UIImage imageWithData:ceche]; component.img=tempImg; }else{ //在分線程中進行圖片數據的獲取 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ if (tempURL) { NSData * data = [YHBaseData getDataWithUrl:tempURL]; if (data) { //獲取完成後村緩存 //作緩存 [[YHBaseCecheCenter sharedTheSingletion]writeCecheFile:data withFileID:tempURL toPath:YHBaseCecheImage]; //賦值 回調代理 UIImage * tempImg = [UIImage imageWithData:data]; component.img=tempImg; //這裏代理是我添加的,當圖片下載完成後 通知視圖從新排版 if ([[rcLabel imageDelegate]respondsToSelector:@selector(YHRTLabelImageSuccess:)]) { //在主線程中執行回調 //這個地方要在主線程中執行,不然刷新會有延時 dispatch_async(dispatch_get_main_queue(), ^{ [[rcLabel imageDelegate] YHRTLabelImageSuccess:rcLabel]; }); } } }; }); } }
經過如上簡單的擴展,基本達到了項目中的需求,這裏把個人一些想法和思路分享給你們,有更好的解決方案,或者同是開發愛好者,歡迎指點與交流,個人QQ是316045346。
專一技術,熱愛生活,交流技術,也作朋友。
——琿少 QQ羣:203317592