配色: 字号:
iOS App开发中扩展RCLabel组件进行基于HTML的文本布局
2016-12-03 | 阅:  转:  |  分享 
  
iOSApp开发中扩展RCLabel组件进行基于HTML的文本布局

RCLabel组件基于CoreText框架,可以将HTML标记的文本内容转为富文本视图,这里我们就来解读如何在iOSApp开发中扩展RCLabel组件进行基于HTML的文本布局:

iOS系统是一个十分注重用户体验的系统,在iOS系统中,用户交互的方案也十分多,然而要在label中的某部分字体中添加交互行为确实不容易的,如果使用其他类似Button的控件来模拟,文字的排版又将是一个解决十分困难的问题。这个问题的由来是项目中的一个界面中有一些广告位标签,而这些广告位的标签却是嵌在文本中的,当用户点击文字标签的位置时,会跳转到响应的广告页。

CoreText框架和一些第三方库可以解决这个问题,但直接使用CoreText十分复杂,第三方库多注重于富文本的排版,对类似文字超链接的支持亦不是特别简洁,我们可以借助一些第三方的东西进行针对性更强,更易用的封装。

RCLabel是一个第三方的将html字符串进行文本布局的工具,代码十分轻巧,并且其是基于CoreText框架的,其原生性和扩展性十分强。



一、扩展于RCLabel的支持异步加载网络图片的富文本引擎的设计

在iOS开发中,图文混排一直都是UI编程的一个核心点,也有许多优秀的第三方引擎,其中很有名的一套图文混排的框架叫做DTCoreText。但是在前些日的做的一个项目中,我并没有采用这套框架,原因有二,一是这套框架体积非常大,而项目的需求其实并不太高;二是要在这套框架中修改一些东西,难度也非常大,我最终采用的是一个叫做RCLabel的第三方控件,经过一些简单的优化和完善,达到了项目的要求。

先来介绍一下我项目中的图文混排的需求:首先我从服务器中取到的数据是字符串,但是其中穿插图片的位置是一个HTML的图片标签,标签里的资源路径就是图片的请求地址。需要达到的要求是这些数据显示出来后,图片的位置要空出来,然后通过异步的网络请求获取图片的数据,再将图片插入文字中。

要自己实现一套这样的引擎确实会比较麻烦,幸运的是RCLabel可以完美的帮我们解析带有HTML标签的数据,进行图文混排,我们先来看一下这个东西怎么用,下面是我封装的一个展示html数据的view:

@interfaceYHBaseHtmlView()

{

//RCLabel对象

RCLabel_rcLabel;

//保存属性用于异步加载完成后刷新

RTLabelComponentsStructure_origenComponent;

//含html标签的数据字符串

NSString_srt;

}

@end

@implementationYHBaseHtmlView

/

//OnlyoverridedrawRect:ifyouperformcustomdrawing.

//Anemptyimplementationadverselyaffectsperformanceduringanimation.

-(void)drawRect:(CGRect)rect{

//Drawingcode

}

/

-(instancetype)initWithCoder:(NSCoder)coder

{

self=[superinitWithCoder:coder];

if(self){

//将rclabel初始化

_rcLabel=[[RCLabelalloc]init];

[selfaddSubview:_rcLabel];

}

returnself;

}

-(instancetype)initWithFrame:(CGRect)frame

{

self=[superinitWithFrame:frame];

if(self){

_rcLabel=[[RCLabelalloc]initWithFrame:frame];

[selfaddSubview:_rcLabel];

}

returnself;

}

-(void)reSetHtmlStr:(NSString)htmlStr{

_srt=htmlStr;

//这个代理是我额外添加的后面解释

_rcLabel.imageDelegate=self;

//设置frame

_rcLabel.frame=CGRectMake(0,0,self.frame.size.width,0);

//设置属性

_origenComponent=[RCLabelextractTextStyle:htmlStrIsLocation:NOwithRCLabel:_rcLabel];

_rcLabel.componentsAndPlainText=_origenComponent;

//获取排版后的size

CGSizesize=[_rcLabeloptimumSize];

//重新设置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=[RCLabelextractTextStyle:_srtIsLocation:NOwithRCLabel:_rcLabel];

_rcLabel.componentsAndPlainText=_origenComponent;



CGSizesize=[_rcLabeloptimumSize];

_rcLabel.frame=CGRectMake(0,0,_rcLabel.frame.size.width,size.height);

self.frame=_rcLabel.frame;

if([self.delegaterespondsToSelector:@selector(YHBaseHtmlView:SizeChanged:)]){

[self.delegateYHBaseHtmlView:selfSizeChanged:self.frame.size];

}

}

RCLabel的用法很简单,总结来说只有三步:

1.初始化并设置frame

2.通过带html标签的数据进行属性的初始化

3.将属性进行set设置并重设视图frame

RCLabel是很强大,并且代码很简练,但是其中处理图片的部分必须是本地的图片,即图片html标签中的路径必须是本地图片的名字,其内部是通过[UIImageImageNamed:]这个方法进行图片的渲染的,所以要达到我们的需要,我们需要对其进行一些简单的扩展:

1、在属性设置方法中添加一个参数,来区分本地图片与网络图片:

//我在这个方法中添加了location这个bool值,实际上rclabel这个参数也是我添加的,是为了后面代理使用的

+(RTLabelComponentsStructure)extractTextStyle:(NSString)dataimageIsLocation:(BOOL)locationwithRCLabel:(RCLabel)rcLabel;

2、在实现方法中添加如下代码,因为原文件有1900多行,在其中弄清楚逻辑关系也确实费了我不小的力气,我这里只将我添加的代码贴过来

#warning这里进行了兼容性处理

if(location){

//本地图片的渲染

if(tempURL){

UIImagetempImg=[UIImageimageNamed:tempURL];

component.img=tempImg;



}

}else{//这里做远程图片数据的处理

//这里我进行了缓存的操作,这个缓存中心是我封装的框架中的另一套东西,这里可以不用在意

//先读缓存

NSDataceche=[[YHBaseCecheCentersharedTheSingletion]readCecheFile:tempURLfromPath:YHBaseCecheImage];

if(ceche){

UIImagetempImg=[UIImageimageWithData:ceche];

component.img=tempImg;

}else{

//在分线程中进行图片数据的获取

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0),^{

if(tempURL){

NSDatadata=[YHBaseDatagetDataWithUrl:tempURL];

if(data){

//获取完成后村缓存

//做缓存

[[YHBaseCecheCentersharedTheSingletion]writeCecheFile:datawithFileID:tempURLtoPath:YHBaseCecheImage];

//赋值回调代理

UIImagetempImg=[UIImageimageWithData:data];

component.img=tempImg;

//这里代理是我添加的,当图片下载完成后通知视图重新排版

if([[rcLabelimageDelegate]respondsToSelector:@selector(YHRTLabelImageSuccess:)]){

//在主线程中执行回调

//这个地方要在主线程中执行,否则刷新会有延时

dispatch_async(dispatch_get_main_queue(),^{

[[rcLabelimageDelegate]YHRTLabelImageSuccess:rcLabel];

});



}



}



};



});

}



}



二、视图类与模型类的设计

RCLabel的核心之处在于将HTML文本转换为富文本布局视图,因此我们可以将要显示的文本编程html字符串,将其可以进行用户交互的部分进行html超链接关联,RCLabel就检测到我们点击的区域进行响应逻辑的回调。设计类如下:

.h文件

//文本与超链接地址关联的model类后面会说

@classYHBaseLinkingLabelModel;

@protocolYHBaseLinkingLabelProtocol

@optional

/

点击超链接后出发的代理方法model中有链接地址和文字

/

-(void)YHBaseLinkingLabelClickLinking:(YHBaseLinkingLabelModel)model;

/

尺寸改变后出发的方法

/

-(void)YHBaseLinkingLabelSizeChange:(CGSize)size;

@end

@interfaceYHBaseLinkingLabel:YHBaseView

/

文字数组里面存放这文字对应的超链接对象

/

@property(nonatomic,strong)NSArraytextArray;

@property(nonatomic,weak)iddelegate;

/

设置文字颜色

/

@property(nonatomic,strong)UIColortextColor;

/

设置超链接文字颜色

/

@property(nonatomic,strong)UIColorlinkColor;

/

设置字体大小

/

@property(nonatomic,assign)NSUIntegerfontSize;

/

设置超链接字体大小

/

@property(nonatomic,assign)intlinkingFontSize;

/

设置是否显示下划线

/

@property(nonatomic,assign)BOOLisShowUnderLine;

@end

.m文件

@interfaceYHBaseLinkingLabel()

@end

@implementationYHBaseLinkingLabel

{

//以前博客中封装的显示HTML字符串富文本的视图

YHBaseHtmlView_label;

}

/

//重载一些初始化方法

-(instancetype)init

{

self=[superinit];

if(self){

_label=[[YHBaseHtmlViewalloc]init];

[selfaddSubview:_label];

[_labelmas_makeConstraints:^(MASConstraintMakermake){

make.leading.equalTo(@0);

make.trailing.equalTo(@0);

make.top.equalTo(@0);

make.bottom.equalTo(@0);

}];

_label.delegate=self;

}

returnself;

}

-(instancetype)initWithCoder:(NSCoder)coder

{

self=[superinitWithCoder:coder];

if(self){

_label=[[YHBaseHtmlViewalloc]init];

[selfaddSubview:_label];

[_labelmas_makeConstraints:^(MASConstraintMakermake){

make.leading.equalTo(@0);

make.trailing.equalTo(@0);

make.top.equalTo(@0);

make.bottom.equalTo(@0);

}];

_label.delegate=self;

}

returnself;

}

-(instancetype)initWithFrame:(CGRect)frame

{

self=[superinitWithFrame:frame];

if(self){

_label=[[YHBaseHtmlViewalloc]init];

[selfaddSubview:_label];

[_labelmas_makeConstraints:^(MASConstraintMakermake){

make.leading.equalTo(@0);

make.trailing.equalTo(@0);

make.top.equalTo(@0);

make.bottom.equalTo(@0);

}];

_label.delegate=self;

}

returnself;

}

//设置文本数组

-(void)setTextArray:(NSArray)textArray{

_textArray=textArray;

//进行html转换

NSStringhtmlString=[selftransLinkingDataToHtmlStr:textArray];

//进行布局

[_labelreSetHtmlStr:htmlString];



}

-(void)setTextColor:(UIColor)textColor{

_textColor=textColor;

_label.fontColor=textColor;

}

-(void)setLinkColor:(UIColor)linkColor{

_linkColor=linkColor;

_label.linkingColor=linkColor;

}

-(void)setFontSize:(NSUInteger)fontSize{

_fontSize=fontSize;

[_labelsetFontSize:(int)fontSize];

}

-(void)setLinkingFontSize:(int)linkingFontSize{

_linkingFontSize=linkingFontSize;

[_labelsetLinkingSize:linkingFontSize];

}

-(void)setIsShowUnderLine:(BOOL)isShowUnderLine{

_isShowUnderLine=isShowUnderLine;

[_labelsetShowUnderLine:isShowUnderLine];

}

-(NSString)transLinkingDataToHtmlStr:(NSArray)data{

NSMutableStringmutStr=[[NSMutableStringalloc]init];

for(inti=0;i
//这个model中存放的是超链接部分的文字和对应的url

YHBaseLinkingLabelModelmodel=data[i];

if(!model.linking){

[mutStrappendString:model.text];

}else{

[mutStrappendString:@"
[mutStrappendString:model.linking];

[mutStrappendString:@">"];

[mutStrappendString:model.text];

[mutStrappendString:@""];

}

}

returnmutStr;

}

#pragmamarkdelegate

//点击的回调

-(void)YHBaseHtmlView:(YHBaseHtmlView)htmlViewClickLink:(NSString)url{

for(YHBaseLinkingLabelModelmodelin_textArray){

if([model.linkingisEqualToString:url]){

if([self.delegaterespondsToSelector:@selector(YHBaseLinkingLabelwww.visa158.comClickLinking:)]){

[self.delegateYHBaseLinkingLabelClickLinking:model];

return;

}

}

}

}

//布局尺寸改变的回调

-(void)YHBaseHtmlView:(YHBaseHtmlView)htmlViewSizeChanged:(CGSize)size{

if([self.delegaterespondsToSelector:@selector(YHBaseLinkingLabelSizeChange:)]){

[self.delegateYHBaseLinkingLabelSizeChange:size];

}

}

@end

上面我们有用到一个YHBaseLinkingLabelModel类,这个类进行了链接与字符的映射,设计如下:

@interfaceYHBaseLinkingLabelModel:YHBaseModel

/

文字内容

/

@property(nonatomic,strong)NSStringtext;

/

超链接地址nil则为无

/

@property(nonatomic,strong)NSStringlinking;

@end

YHBaseHtmlView类是对RCLabel的一层封装,其中也对RCLabel进行了一些优化和改动,代码较多且在上篇博客中有介绍,这里不再多做解释了。

在ViewController中写如下代码进行使用:

-(void)viewDidLoad{

[superviewDidLoad];

//Doanyadditionalsetupafterloadingtheview,typicallyfromanib.

YHBaseLinkingLabellabel=[[YHBaseLinkingLabelalloc]initWithFrame:CGRectMake(100,100,200,100)];

NSMutableArrayarray=[[NSMutableArrayalloc]init];

for(inti=0;i<6;i++){

YHBaseLinkingLabelModelmodel=[[YHBaseLinkingLabelModelalloc]www.hunanwang.netinit];

if(!(i%2)){

model.text=[NSStringstringWithFormat:@"第%d个标签",i];

model.linking=[NSStringstringWithFormat:@"第%d个标签",i];

}else{

model.text=@",不能点得文字,";

}

[arrayaddObject:model];

}

label.textColor=[UIColorblackColor];

label.linkColor=[UIColorpurpleColor];

label.fontSize=15;

label.linkingFontSize=17;

label.isShowUnderLine=YES;

label.delegate=self;

label.textArray=array;

[self.viewaddSubview:label];



}

-(void)YHBaseLinkingLabelClickLinking:(YHBaseLinkingLabelModel)model{

NSLog(@"%@",model.linking);

}























献花(0)
+1
(本文系白狐一梦首藏)