CoreText 解析富文本實(shí)戰(zhàn)

CoreText是專門用來處理文字和字體的底層技術(shù)篮赢。直接和2D 圖形渲染引擎打交道郎笆。效率非常高。 先看實(shí)戰(zhàn)效果https://github.com/FSilver/FWDrawView

第一步:配置Config

@interface FWDrawConfig : NSObject

@property(nonatomic,strong)NSString *text;
@property(nonatomic,assign)CGFloat width;
@property(nonatomic,assign)UIFont  *font;
@property(nonatomic,strong)UIColor *textColor;
@property(nonatomic,strong)UIColor *linkColor;
@property(nonatomic,assign)int underLineOfLink;
@property(nonatomic,assign)NSInteger numberOfLines; 
@property(nonatomic,assign)UIEdgeInsets edgInsets;

@end

拿到文本后,先設(shè)置文本最大的顯示寬度脚作,文本的字體警没,顏色匈辱,文字最大顯示行數(shù),文字在控件中上下左右邊距杀迹。 鏈接字體的顏色亡脸,下劃線。

第二步:解析parse

@interface FWDrawParser : NSObject

@property(nonatomic,strong)FWDrawInfo *data;
@property(nonatomic,strong)NSMutableAttributedString *resultAttributeString;
@property(nonatomic,strong)NSDictionary *textAttributeDict;

-(id)initWithConfig:(FWDrawConfig*)config;
-(void)parseEmoji;
-(void)parseUrl;
-(void)parsePhone;
-(void)addLinkWithValue:(id)value range:(NSRange)range;

@end

利用config生成富文本字典树酪,以及富文本呢

-(void)configTextAttributeDictionay
{
    //用一個普通的漢子排版的高度浅碾,設(shè)置為最低高度,保證中文续语,英文垂谢,表情,間距相等疮茄,視覺上舒服
    //float minLineHight = config.font.lineHeight - config.font.descender;
    float minLineHight = _config.font.lineHeight;
    
    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    [paragraphStyle setLineSpacing:0];
    [paragraphStyle setMinimumLineHeight:minLineHight];
    paragraphStyle.lineHeightMultiple = 0;
    
    NSDictionary *ats = @{
                          NSFontAttributeName : _config.font,
                          NSForegroundColorAttributeName:_config.textColor,
                          NSParagraphStyleAttributeName : paragraphStyle,
                          };
    _textAttributeDict = ats;
}

-(void)configResultAttributeString
{
    _resultAttributeString = [[NSMutableAttributedString alloc]init];
    NSAttributedString *attributeString = [[NSAttributedString alloc]initWithString:_config.text attributes:_textAttributeDict];
    [_resultAttributeString appendAttributedString:attributeString];
}

第三步:數(shù)據(jù)data

@interface FWDrawInfo : NSObject

@property(nonatomic,assign)CTFrameRef ctFrame;
@property(nonatomic,assign)CGFloat originHeight; //全部顯示完全的高度
@property(nonatomic,assign)CGFloat width;        //實(shí)際寬度
@property(nonatomic,assign)CGFloat height;       //實(shí)際高度
@property(nonatomic,assign)NSInteger lines;         //文本總行數(shù)
@property(nonatomic,assign)NSInteger numberOfLines; //限制行數(shù)
@property(nonatomic,assign)UIEdgeInsets edgInsets;
@property(nonatomic,strong)NSDictionary *textAttributeDict;
@property(nonatomic,strong)NSAttributedString *attributedString;

@property(nonatomic,strong)NSArray *emojiArray;  //賦值的時候滥朱,計(jì)算區(qū)域
@property(nonatomic,strong)NSArray *linkArray;   //賦值的時候,計(jì)算區(qū)域

@end

第四步:繪制data

@class FWDrawView;
@protocol FWDrawViewDelegate <NSObject>

@optional
-(void)didClickFWDraw:(FWDrawView*)draw; //單擊
-(void)didLongPressFWDraw:(FWDrawView*)draw;//雙擊
-(void)didClickFWDraw:(FWDrawView *)draw byLink:(FWDrawLinkInfo*)link; //點(diǎn)擊鏈接

@end

@interface FWDrawView : UIView

@property(nonatomic,weak)id<FWDrawViewDelegate>delegate;
@property(nonatomic,strong)FWDrawInfo *data;

@property(nonatomic,strong)UIColor *linkBgColor;//選中鏈接后力试,鏈接背景色

@property(nonatomic,strong)UIColor *tapColor; //點(diǎn)擊View,高亮色
@property(nonatomic,assign)BOOL allowTapGesture; //是否允許單機(jī)

@property(nonatomic,assign)BOOL allowLongPressGesture;  //是否允許長按 
@property(nonatomic,strong)UIColor *longPressColor;     //長按View后焚虱,view的背景色

@end

繪制方法

-(void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    
    if(self.data == nil){
        return;
    }
    
    CGContextRef  context = UIGraphicsGetCurrentContext();
    //坐標(biāo)系coreText --》 UIkit坐標(biāo)系
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    
    _lastLineOriginY = -1;
    _lastLineRightX = -1;
    if(_data.numberOfLines == 0 || _data.numberOfLines >= _data.lines){
        
        CTFrameDraw(self.data.ctFrame, context);
        
    }else{
        
        [self drawLineByContext:context];
    }
    

    //作用是,畫表情
    for (FWDrawEmojiInfo * emoji in self.data.emojiArray) {
        
        
        //轉(zhuǎn)為UI坐標(biāo)系,表情位置大于有效區(qū)域懂版,就break
        float y = _data.height - emoji.drawRect.origin.y - emoji.drawRect.size.height;
        if(y + emoji.drawRect.size.height > _data.height - _data.edgInsets.bottom){
            break;
        }
    
        BOOL drawImage = NO;
        if(_lastLineOriginY == -1 && _lastLineRightX == -1){
            //全部顯示
            drawImage = YES;
            
        }else{
            //限制行數(shù)
            if(y < _lastLineOriginY){
                //不是最后一行
                drawImage = YES;
            }else if(emoji.drawRect.origin.x + emoji.drawRect.size.width < _lastLineRightX){
                //是最后一行
                 drawImage = YES;
            }
        }
        
        if(drawImage){
            UIImage *image = [UIImage imageNamed:emoji.imageName];
            if(image){
                CGContextDrawImage(context, emoji.drawRect, image.CGImage);
            }
        }
      }
    
    
    //作用是鹃栽,選中鏈接后,鏈接高亮顯示
    for (FWDrawLinkInfo *url in self.data.linkArray) {
        
        for (NSValue  *value in url.drawRectArray) {
            
            
            //轉(zhuǎn)為UI坐標(biāo)系
            CGRect urlRect = [value CGRectValue];
            CGFloat y = _data.height - urlRect.origin.y - urlRect.size.height;
            if( y + urlRect.size.height > _data.height - _data.edgInsets.bottom){
                break;
            }
            
            if(url.isSelected){
            
                CGRect urlRect = [value CGRectValue];
                CGContextSetFillColorWithColor(context,self.linkBgColor.CGColor);
                CGContextFillRect(context , urlRect);
            }
        }
    }
}

完整代碼github地址 https://github.com/FSilver/FWDrawView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末躯畴,一起剝皮案震驚了整個濱河市民鼓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蓬抄,老刑警劉巖丰嘉,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嚷缭,居然都是意外死亡饮亏,警方通過查閱死者的電腦和手機(jī)耍贾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來路幸,“玉大人荐开,你說我怎么就攤上這事〖螂龋” “怎么了晃听?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長砰识。 經(jīng)常有香客問我能扒,道長,這世上最難降的妖魔是什么辫狼? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任初斑,我火速辦了婚禮,結(jié)果婚禮上膨处,老公的妹妹穿的比我還像新娘见秤。我一直安慰自己,他們只是感情好灵迫,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布秦叛。 她就那樣靜靜地躺著,像睡著了一般瀑粥。 火紅的嫁衣襯著肌膚如雪挣跋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天狞换,我揣著相機(jī)與錄音避咆,去河邊找鬼。 笑死修噪,一個胖子當(dāng)著我的面吹牛查库,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播黄琼,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼樊销,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脏款?” 一聲冷哼從身側(cè)響起围苫,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撤师,沒想到半個月后剂府,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剃盾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年腺占,在試婚紗的時候發(fā)現(xiàn)自己被綠了淤袜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡衰伯,死狀恐怖铡羡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嚎研,我是刑警寧澤蓖墅,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布库倘,位于F島的核電站临扮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏教翩。R本人自食惡果不足惜杆勇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饱亿。 院中可真熱鬧蚜退,春花似錦、人聲如沸彪笼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽配猫。三九已至幅恋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泵肄,已是汗流浹背捆交。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腐巢,地道東北人品追。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像冯丙,于是被迫代替她去往敵國和親肉瓦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容