CJLabel圖文混排二 —— UILabel插入圖片以及精確鏈點(diǎn)點(diǎn)擊

上一篇文章介紹了CJLabel的實(shí)現(xiàn)跳芳,在V2.0.0版本之前,CJLabel的圖文顯示是基于NSAttributedString來實(shí)現(xiàn)的竹勉,但有若干不足:

  1. 圖片點(diǎn)擊響應(yīng)只支持emoji表情飞盆,對于NSAttributedString中通過NSTextAttachment添加的圖片,無法響應(yīng)點(diǎn)擊饶米。
  2. 點(diǎn)擊鏈點(diǎn)的判斷:核心方法是調(diào)用CTLineGetStringIndexForPosition()獲取當(dāng)前觸摸點(diǎn)CGPoint在NSString中對應(yīng)的index桨啃,再判斷該index是否屬于點(diǎn)擊鏈點(diǎn)的NSRange。然而當(dāng)文本中包含多個(gè)emoji表情時(shí)檬输,CTLineGetStringIndexForPosition()的獲取并不準(zhǔn)確照瘾,特別是如果當(dāng)前點(diǎn)擊鏈點(diǎn)處在文本的最后一行時(shí),存在較大的誤差丧慈。
  3. 無法響應(yīng)長按點(diǎn)擊析命,另外點(diǎn)擊鏈點(diǎn)不能設(shè)置點(diǎn)擊高亮屬性主卫。

針對以上問題,我對CJLabel 進(jìn)行了重構(gòu)優(yōu)化鹃愤,下面就來看看CJLabel 新的實(shí)現(xiàn)簇搅。

CJLabel1.gif
CJLabel2.gif

開始之前先來看幾張圖(某些圖片來自網(wǎng)絡(luò))

字形與字符
字形描述屬性.jpg
  1. Bounding Box(邊界框 bbox),這是一個(gè)假想的框子软吐,它盡可能緊密的將字形包括瘩将。
  2. Baseline(基線),一條假想的線,一行上的字形都以此線作為上下位置的參考凹耙。
  3. Origin(每一行的原點(diǎn))姿现,Origin是在圖中的Baseline的左側(cè)。
  4. Ascent(上行高度)從原點(diǎn)到字體中最高(這里的高深都是以基線為參照線的)的字形的頂部的距離肖抱,Ascent是一個(gè)正值备典。
  5. Descent(下行高度)從原點(diǎn)到字體中最深的字形底部的距離,Descent是一個(gè)負(fù)值意述。
  6. Linegap(行距)提佣,Linegap也可以稱作leading(其實(shí)準(zhǔn)確點(diǎn)講應(yīng)該叫做External leading)。
    </br>


    字符描述屬性.jpg

    上圖中綠色線條表示基線荤崇,黃色線條表示下行高度拌屏,綠色線條到紅框最頂部的距離為上行高度,而黃色線條到紅框底部的距離為行間距术荤。因此行高的計(jì)算公式是lineHeight = Ascent + |Descent| + Leading

CoreText繪制NSAttributedString
CTFrame.png

CoreText是iOS/OSX里的文字渲染引擎槐壳,它會把一行里連在一起相同屬性的文字合在一起作為一個(gè)CTRun,每一行是一個(gè)CTLine喜每,多行合在一起組成CTFrame务唐。如上圖,第一行的文字有兩種樣式带兜,第一部分是加粗枫笛,第二部分是斜體,因?yàn)闃邮讲煌苑殖闪藘蓚€(gè)CTRun刚照,CTLine包含了這兩個(gè)CTRun刑巧,CTFrame包含了所有CTLine。



NSAttributeString可以通過CoreText提供的方法生成CTFramesetter无畔,CTFramesetter是用于創(chuàng)建CTFrame的工廠啊楚,給CTFramesetter一個(gè)CGPath,或者簡單理解為給他一個(gè)框框浑彰,它就會通過它持有的CTTypesetter生成CTFrame恭理,CTFrame生成時(shí)里面包含的CTLine和CTRun就全部生成好了,可以直接繪制到畫布上郭变。CTFrame/CTLine/CTRun都提供了渲染接口颜价,但前兩者是封裝涯保,最后實(shí)際都是調(diào)用到CTRun的渲染接口去繪制。

新建UILabel子類周伦,重寫- (void)drawTextInRect:(CGRect)rect繪制NSAttributeString

測試.png

- (void)drawTextInRect:(CGRect)rect {
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc]initWithString:@"這是一段測試數(shù)據(jù)"];
    [attributedText addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(2, 2)];
    [attributedText addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:15] range:NSMakeRange(2, 2)];
    
    CGContextRef c = UIGraphicsGetCurrentContext();
    // 先將當(dāng)前圖形狀態(tài)推入堆棧
    CGContextSaveGState(c);
    // 設(shè)置字形變換矩陣為CGAffineTransformIdentity夕春,也就是說每一個(gè)字形都不做圖形變換
    CGContextSetTextMatrix(c, CGAffineTransformIdentity);
    // 坐標(biāo)轉(zhuǎn)換,iOS 坐標(biāo)原點(diǎn)在左上角专挪,Mac OS 坐標(biāo)原點(diǎn)在左下角
    CGContextTranslateCTM(c, 0.0f, rect.size.height);
    // CTM 坐標(biāo)移翻轉(zhuǎn)
    CGContextScaleCTM(c, 1.0f, -1.0f);
    
    //生成 CTFramesetterRef
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attributedText);
    CFRange textRange = CFRangeMake(0, (CFIndex)[attributedText length]);
    // 獲取path
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);
    //生成 CTFrameRef
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, textRange, path, NULL);
    //獲取所有行
    CFArrayRef lines = CTFrameGetLines(frame);
    //原點(diǎn)數(shù)組
    CGPoint lineOrigins[CFArrayGetCount(lines)];
    //獲取所有CTLineRef的原點(diǎn)
    CTFrameGetLineOrigins(frame, CFRangeMake(0, CFArrayGetCount(lines)), lineOrigins);
    for (CFIndex lineIndex = 0; lineIndex < CFArrayGetCount(lines); lineIndex++) {
        CGPoint lineOrigin = lineOrigins[lineIndex];
        CGContextSetTextPosition(c, lineOrigin.x, lineOrigin.y);
        CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);
        //繪制當(dāng)前行
        CTLineDraw(line, c);
    }
    CFRelease(frame);
    CGPathRelease(path);
    CFRelease(framesetter);
    CGContextRestoreGState(c);
}
NSAttributedString圖文混排

插入圖片.jpg

NSAttributedString插入圖片可以通過CTRunDelegateCallbacks來實(shí)現(xiàn)及志,如上圖所示,藍(lán)色CTRun設(shè)置了CTRunDelegateRef寨腔,并在需要插入圖片的位置以空白字符串代替困肩。CoreText逐行繪制文字,當(dāng)繪制到藍(lán)色CTRun時(shí)脆侮,會觸發(fā)CTRunDelegateCallbacks的相關(guān)回調(diào):

void RunDelegateDeallocCallback(void * refCon) {
}
//獲取圖片高度
CGFloat RunDelegateGetAscentCallback(void * refCon) {
    return [(NSNumber *)[(__bridge NSDictionary *)refCon objectForKey:kCJImageHeight] floatValue];
}
CGFloat RunDelegateGetDescentCallback(void * refCon) {
    return 0;
}
//獲取圖片寬度
CGFloat RunDelegateGetWidthCallback(void * refCon) {
    return [(NSNumber *)[(__bridge NSDictionary *)refCon objectForKey:kCJImageWidth] floatValue];
}

在指定位置插入圖片

/**
 指定位置插入圖片

 @param line 當(dāng)前繪制行
 @param c CGContextRef
 @param lineOrigins CTLine坐標(biāo)原點(diǎn)
 @param lineIndex 第幾行
 */
- (void)drawImageLine:(CTLineRef)line
              context:(CGContextRef)c
          lineOrigins:(CGPoint[])lineOrigins
            lineIndex:(CFIndex)lineIndex
{
    CFArrayRef runs = CTLineGetGlyphRuns(line);
    for (int j = 0; j < CFArrayGetCount(runs); j++) {
        CGFloat runAscent;
        CGFloat runDescent;
        CGPoint lineOrigin = lineOrigins[lineIndex];
        //獲取每個(gè)CTRun
        CTRunRef run = CFArrayGetValueAtIndex(runs, j);
        NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
        CGRect runRect;
        //調(diào)整CTRun的rect
        runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
        
        runRect = CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runRect.size.width, runAscent + runDescent);
        
        NSDictionary *imgInfoDic = attributes[kCJImageAttributeName];
        if (imgInfoDic[kCJImageName]) {
            UIImage *image = [UIImage imageNamed:imgInfoDic[kCJImageName]];
            if (image) {
                CGRect imageDrawRect;
                CGFloat imageSizeWidth = ceil(runRect.size.width);
                CGFloat imageSizeHeight = ceil(runRect.size.height);
                imageDrawRect.size = CGSizeMake(imageSizeWidth, imageSizeHeight);
                imageDrawRect.origin.x = runRect.origin.x + lineOrigin.x;
                imageDrawRect.origin.y = lineOrigin.y;
                CGContextDrawImage(c, imageDrawRect, image.CGImage);
            }
        }
    }
}

CJLabel點(diǎn)擊鏈點(diǎn)的判斷

前面我們了解了CoreText在UILabel上面的圖文混排繪制的過程,而且在繪制過程中細(xì)化到了每一行CTLineCTRun勇劣,那何不將點(diǎn)擊鏈點(diǎn)對應(yīng)的CTRun也做上標(biāo)記靖避;在繪制的過程中判斷到當(dāng)前CTRun是點(diǎn)擊鏈點(diǎn)則將其對應(yīng)的CGRect保存到NSArray中。
再在- touchesBegan: withEvent:事件中判斷觸摸點(diǎn)是否在NSArray里的CGRect里面比默,核心調(diào)用方法CGRectContainsPoint(bounds, point)幻捏。
這樣就徹底告別了CTLineGetStringIndexForPosition()的判斷邏輯,而且CGRectContainsPoint的判斷準(zhǔn)確度更高命咐。

對于如何標(biāo)記點(diǎn)擊鏈點(diǎn)的CTRun篡九,這就輪到NSMutableAttributedString上場了,調(diào)用它的實(shí)例方法- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range醋奠,可以在指定位置添加自定義屬性(前面說到的標(biāo)記插入圖片榛臼,也就是這樣做的)。

*** 另外CJLabel新增了以下屬性:***
新增屬性能夠?qū)χ付ㄎ恢玫淖址砑颖尘吧芩尽⒚柽吪嫔啤Ⅻc(diǎn)擊高亮的效果。

/**
 背景填充顏色塞祈。值為UIColor金刁。默認(rèn) `nil`。
 該屬性優(yōu)先級低于NSBackgroundColorAttributeName议薪,如果設(shè)置NSBackgroundColorAttributeName會覆蓋kCJBackgroundFillColorAttributeName
 */
extern NSString * const kCJBackgroundFillColorAttributeName;

/**
 背景邊框線顏色尤蛮。值為UIColor。默認(rèn) `nil`
 */
extern NSString * const kCJBackgroundStrokeColorAttributeName;

/**
 背景邊框線寬度斯议。值為NSNumber产捞。默認(rèn) `1.0f`
 */
extern NSString * const kCJBackgroundLineWidthAttributeName;

/**
 背景邊框線圓角角度。值為NSNumber哼御。默認(rèn) `5.0f`
 */
extern NSString * const kCJBackgroundLineCornerRadiusAttributeName;

/**
 點(diǎn)擊時(shí)候的背景填充顏色轧葛。值為UIColor搂抒。默認(rèn) `nil`。
 該屬性優(yōu)先級低于NSBackgroundColorAttributeName尿扯,如果設(shè)置NSBackgroundColorAttributeName會覆蓋kCJActiveBackgroundFillColorAttributeName
 */
extern NSString * const kCJActiveBackgroundFillColorAttributeName;

/**
 點(diǎn)擊時(shí)候的背景邊框線顏色求晶。值為UIColor。默認(rèn) `nil`
 */
extern NSString * const kCJActiveBackgroundStrokeColorAttributeName;

CJLabel實(shí)現(xiàn)邏輯

實(shí)現(xiàn)邏輯總結(jié)如下:

  1. 在NSAttributedString中對點(diǎn)擊鏈點(diǎn)衷笋、插入圖片芳杏、添加圓角背景或者描邊的字符進(jìn)行標(biāo)記;
  2. 根據(jù)NSAttributedString中的標(biāo)記辟宗,獲取對應(yīng)字符在label中的CGRect爵赵、自定義參數(shù)、圖片名稱等信息泊脐,并保存在NSArray中空幻;
  3. 逐行繪制CTLine。CoreText先在指定位置填充背景色容客,然后繪制文字秕铛,接著在插入圖片的位置繪制圖片,最后在指定位置添加描邊(CoreText繪制是疊加覆蓋的缩挑,要注意層級關(guān)系)但两;
  4. 點(diǎn)擊label,在- touchesBegan:withEvent:以及UILongPressGestureRecognizer長按回調(diào)事件中供置,將觸摸點(diǎn)CGPoint與CGRect數(shù)組做遍歷判斷CGRectContainsPoint(bounds, point)谨湘,找到對應(yīng)字符并響應(yīng)回調(diào)事件。

當(dāng)然實(shí)現(xiàn)細(xì)節(jié)中還包括了不少其他的復(fù)雜邏輯處理芥丧,這里就不一一說明了紧阔,有興趣的可以下載源碼看看。


CJLabel類

CJLabel響應(yīng)點(diǎn)擊以及長按點(diǎn)擊事件续担,回調(diào)事件可選擇CJLabelLinkModelBlockCJLabelLinkDelegate來實(shí)現(xiàn)寓辱。

    @protocol CJLabelLinkDelegate <NSObject>
@optional
/**
 點(diǎn)擊鏈點(diǎn)回調(diào)

 @param label 點(diǎn)擊label
 @param linkModel 鏈點(diǎn)model
 */
- (void)CJLable:(CJLabel *)label didClickLink:(CJLabelLinkModel *)linkModel;

/**
 長按點(diǎn)擊鏈點(diǎn)回調(diào)

 @param label 點(diǎn)擊label
 @param linkModel 鏈點(diǎn)model
 */
- (void)CJLable:(CJLabel *)label didLongPressLink:(CJLabelLinkModel *)linkModel;
@end


IB_DESIGNABLE
/**
 * CJLabel 繼承自 UILabel,其文本繪制基于NSAttributedString實(shí)現(xiàn)赤拒,同時(shí)增加了圖文混排秫筏、富文本展示以及添加自定義點(diǎn)擊鏈點(diǎn)并設(shè)置點(diǎn)擊鏈點(diǎn)文本屬性的功能。
 *
 *
 * CJLabel 與 UILabel 不同點(diǎn):
 *
   1. `- init` 不可直接調(diào)用init初始化挎挖,請使用`initWithFrame:` 或 `initWithCoder:`这敬,以便完成相關(guān)初始屬性設(shè)置
 
   2. `attributedText` 與 `text` 均可設(shè)置文本,注意 [self setText:text]中 text類型只能是NSAttributedString或NSString
 
   3. `NSAttributedString`不再通過`NSTextAttachment`顯示圖片(使用`NSTextAttachment`不會起效)蕉朵,請調(diào)用
      `- configureAttributedString: addImageName: imageSize: atIndex: attributes:`或者
      `- configureLinkAttributedString: addImageName: imageSize: atIndex: linkAttributes: activeLinkAttributes: parameter: clickLinkBlock: longPressBlock:`方法添加圖片
 
   4. 新增`extendsLinkTouchArea`崔涂, 設(shè)置是否加大點(diǎn)擊響應(yīng)范圍,類似于UIWebView的鏈點(diǎn)點(diǎn)擊效果
 
   5. 新增`shadowRadius`始衅, 設(shè)置文本陰影模糊半徑冷蚂,可與 `shadowColor`刮萌、`shadowOffset` 配合設(shè)置羊壹,注意改設(shè)置將對全局文本起效
 
   6. 新增`textInsets` 設(shè)置文本內(nèi)邊距
 
   7. 新增`verticalAlignment` 設(shè)置垂直方向的文本對齊方式
 
   8. 新增`delegate` 點(diǎn)擊鏈點(diǎn)代理
 *
 *
 * CJLabel 已知bug:
 *
   `numberOfLines`大于0且小于實(shí)際`label.numberOfLines`韩容,同時(shí)`verticalAlignment`不等于`CJContentVerticalAlignmentTop`時(shí)汞舱,文本顯示位置有偏差
 *
 */
@interface CJLabel : UILabel

/**
 * 指定初始化函數(shù)為 initWithFrame: 或 initWithCoder:
 * 直接調(diào)用 init 會忽略相關(guān)屬性的設(shè)置,所以不能直接調(diào)用 init 初始化.
 */
- (instancetype)init NS_UNAVAILABLE;

/**
 * 對應(yīng)UILabel的attributedText屬性
 */
@property (readwrite, nonatomic, copy) NSAttributedString *attributedText;
/**
 * 對應(yīng)UILabel的text屬性
 */
@property (readwrite, nonatomic, copy) id text;
/**
 * 是否加大點(diǎn)擊響應(yīng)范圍隆夯,類似于UIWebView的鏈點(diǎn)點(diǎn)擊效果钳恕,默認(rèn)NO
 */
@property (readwrite, nonatomic, assign) IBInspectable BOOL extendsLinkTouchArea;
/**
 * 陰影模糊半徑,值為0表示沒有模糊蹄衷,值越大越模糊忧额,該值不能為負(fù), 默認(rèn)值為0愧口。
 * 可與 `shadowColor`睦番、`shadowOffset` 配合設(shè)置
 */
@property (readwrite, nonatomic, assign) CGFloat shadowRadius;
/**
 * 繪制文本的內(nèi)邊距,默認(rèn)UIEdgeInsetsZero
 */
@property (readwrite, nonatomic, assign) UIEdgeInsets textInsets;
/**
 * 當(dāng)text rect 小于 label frame 時(shí)文本在垂直方向的對齊方式耍属,默認(rèn) CJVerticalAlignmentCenter
 */
@property (readwrite, nonatomic, assign) CJLabelVerticalAlignment verticalAlignment;
/**
 點(diǎn)擊鏈點(diǎn)代理對象
 */
@property (readwrite, nonatomic, weak) id<CJLabelLinkDelegate> delegate;

/**
 *  return 計(jì)算NSAttributedString字符串的size大小
 *
 *  @param attributedString NSAttributedString字符串
 *  @param size   預(yù)計(jì)大型邢(比如:CGSizeMake(320, CGFLOAT_MAX))
 *  @param numberOfLines  指定行數(shù)(0表示不限制)
 *
 *  @return 結(jié)果size
 */
+ (CGSize)sizeWithAttributedString:(NSAttributedString *)attributedString
                   withConstraints:(CGSize)size
            limitedToNumberOfLines:(NSUInteger)numberOfLines;

// 在指定位置插入圖片,圖片所在行恬涧,圖文在垂直方向的對齊方式默認(rèn):底部對齊
+ (NSMutableAttributedString *)configureAttributedString:(NSAttributedString *)attrStr
                                            addImageName:(NSString *)imageName
                                               imageSize:(CGSize)size
                                                 atIndex:(NSUInteger)loc
                                              attributes:(NSDictionary *)attributes;

// 在指定位置插入可點(diǎn)擊鏈點(diǎn)圖片,圖片所在行碴巾,圖文在垂直方向的對齊方式默認(rèn):底部對齊
+ (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
                                                addImageName:(NSString *)imageName
                                                   imageSize:(CGSize)size
                                                     atIndex:(NSUInteger)loc
                                              linkAttributes:(NSDictionary *)linkAttributes
                                        activeLinkAttributes:(NSDictionary *)activeLinkAttributes
                                                   parameter:(id)parameter
                                              clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
                                              longPressBlock:(CJLabelLinkModelBlock)longPressBlock;

/**
 在指定位置插入圖片溯捆,并返回插入圖片后的NSMutableAttributedString(圖片占位符所占的NSRange={loc,1})
 
 注意!O闷啊提揍!插入圖片, 如果設(shè)置 NSParagraphStyleAttributeName 屬性煮仇,例如:
 NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
 paragraph.lineBreakMode = NSLineBreakByCharWrapping;
 [attrStr addAttribute:NSParagraphStyleAttributeName value:paragraph range:range];
 請保證 paragraph.lineBreakMode = NSLineBreakByCharWrapping劳跃,不然當(dāng)Label的寬度不夠顯示內(nèi)容或圖片時(shí),不會自動換行, 部分圖片將會看不見
    
 默認(rèn) paragraph.lineBreakMode = NSLineBreakByCharWrapping
 
 @param attrStr 需要插入圖片的NSAttributedString
 @param imageName 圖片名稱
 @param size 圖片大小
 @param loc 圖片插入位置
 @param verticalAlignment 圖片所在行浙垫,圖片與文字在垂直方向的對齊方式(只針對當(dāng)前行)
 @param attributes 圖片文本屬性
 
 @return 插入圖片后的NSMutableAttributedString
 */
+ (NSMutableAttributedString *)configureAttributedString:(NSAttributedString *)attrStr
                                            addImageName:(NSString *)imageName
                                               imageSize:(CGSize)size
                                                 atIndex:(NSUInteger)loc
                                       verticalAlignment:(CJLabelVerticalAlignment)verticalAlignment
                                              attributes:(NSDictionary *)attributes;

/**
 在指定位置插入圖片刨仑,插入圖片為可點(diǎn)擊的鏈點(diǎn)!<欣选杉武!
 返回插入圖片后的NSMutableAttributedString(圖片占位符所占的NSRange={loc,1})
 
 @param attrStr 需要插入圖片的NSAttributedString
 @param imageName 圖片名稱
 @param size 圖片大小
 @param loc 圖片插入位置
 @param verticalAlignment 圖片所在行,圖片與文字在垂直方向的對齊方式(只針對當(dāng)前行)
 @param linkAttributes 圖片鏈點(diǎn)屬性
 @param activeLinkAttributes 點(diǎn)擊狀態(tài)下的圖片鏈點(diǎn)屬性
 @param parameter 鏈點(diǎn)自定義參數(shù)
 @param clickLinkBlock 鏈點(diǎn)點(diǎn)擊回調(diào)
 @param longPressBlock 長按點(diǎn)擊鏈點(diǎn)回調(diào)
 
 @return 插入圖片后的NSMutableAttributedString
 */
+ (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
                                                addImageName:(NSString *)imageName
                                                   imageSize:(CGSize)size
                                                     atIndex:(NSUInteger)loc
                                           verticalAlignment:(CJLabelVerticalAlignment)verticalAlignment
                                              linkAttributes:(NSDictionary *)linkAttributes
                                        activeLinkAttributes:(NSDictionary *)activeLinkAttributes
                                                   parameter:(id)parameter
                                              clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
                                              longPressBlock:(CJLabelLinkModelBlock)longPressBlock;

/**
 根據(jù)指定NSRange配置富文本
 
 @param attrStr NSAttributedString源
 @param range 指定NSRange
 @param attributes 文本屬性
 
 @return 返回新的NSMutableAttributedString
 */
+ (NSMutableAttributedString *)configureAttributedString:(NSAttributedString *)attrStr
                                                 atRange:(NSRange)range
                                              attributes:(NSDictionary *)attributes;

/**
 根據(jù)指定NSRange配置富文本辙售,指定NSRange文本為可點(diǎn)擊鏈點(diǎn)G岜А!旦部!
 
 @param attrStr NSAttributedString源
 @param range 指定NSRange
 @param linkAttributes 鏈點(diǎn)文本屬性
 @param activeLinkAttributes 點(diǎn)擊狀態(tài)下的鏈點(diǎn)文本屬性
 @param parameter 鏈點(diǎn)自定義參數(shù)
 @param clickLinkBlock 鏈點(diǎn)點(diǎn)擊回調(diào)
 @param longPressBlock 長按點(diǎn)擊鏈點(diǎn)回調(diào)
 
 @return 返回新的NSMutableAttributedString
 */
+ (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
                                                     atRange:(NSRange)range
                                              linkAttributes:(NSDictionary *)linkAttributes
                                        activeLinkAttributes:(NSDictionary *)activeLinkAttributes
                                                   parameter:(id)parameter
                                              clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
                                              longPressBlock:(CJLabelLinkModelBlock)longPressBlock;

/**
 對文本中跟withString相同的文字配置富文本
 
 @param attrStr NSAttributedString源
 @param withString 需要設(shè)置的文本
 @param sameStringEnable 文本中所有與withAttString相同的文字是否同步設(shè)置屬性祈搜,sameStringEnable=NO 時(shí)取文本中首次匹配的NSAttributedString
 @param attributes 文本屬性
 
 @return 返回新的NSMutableAttributedString
 */
+ (NSMutableAttributedString *)configureAttributedString:(NSAttributedString *)attrStr
                                              withString:(NSString *)withString
                                        sameStringEnable:(BOOL)sameStringEnable
                                              attributes:(NSDictionary *)attributes;

/**
 對文本中跟withString相同的文字配置富文本较店,指定的文字為可點(diǎn)擊鏈點(diǎn)!H菅唷梁呈!
 
 @param attrStr NSAttributedString源
 @param withString 需要設(shè)置的文本
 @param sameStringEnable 文本中所有與withAttString相同的文字是否同步設(shè)置屬性,sameStringEnable=NO 時(shí)取文本中首次匹配的NSAttributedString
 @param linkAttributes 鏈點(diǎn)文本屬性
 @param activeLinkAttributes 點(diǎn)擊狀態(tài)下的鏈點(diǎn)文本屬性
 @param parameter 鏈點(diǎn)自定義參數(shù)
 @param clickLinkBlock 鏈點(diǎn)點(diǎn)擊回調(diào)
 @param longPressBlock 長按點(diǎn)擊鏈點(diǎn)回調(diào)
 
 @return 返回新的NSMutableAttributedString
 */
+ (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
                                                  withString:(NSString *)withString
                                            sameStringEnable:(BOOL)sameStringEnable
                                              linkAttributes:(NSDictionary *)linkAttributes
                                        activeLinkAttributes:(NSDictionary *)activeLinkAttributes
                                                   parameter:(id)parameter
                                              clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
                                              longPressBlock:(CJLabelLinkModelBlock)longPressBlock;

/**
 *  移除制定range的點(diǎn)擊鏈點(diǎn)
 *
 *  @param range 移除鏈點(diǎn)位置
 *
 *  @return 返回新的NSAttributedString
 */
- (NSAttributedString *)removeLinkAtRange:(NSRange)range;

/**
 *  移除所有點(diǎn)擊鏈點(diǎn)
 *
 *  @return 返回新的NSAttributedString
 */
- (NSAttributedString *)removeAllLink;

@end

CJLabel支持CocoaPods安裝

platform :ios, '7.0'
target 'CJLabelDemo' do
   pod 'CJLabel', '~> 2.1.3'
end

更多實(shí)現(xiàn)細(xì)節(jié)請看Demo

附:
字形與字符的相關(guān)描述引用自 http://blog.cnbang.net/tech/2729/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缰趋,一起剝皮案震驚了整個(gè)濱河市捧杉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌秘血,老刑警劉巖味抖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灰粮,居然都是意外死亡仔涩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門粘舟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熔脂,“玉大人,你說我怎么就攤上這事柑肴∠既啵” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵晰骑,是天一觀的道長适秩。 經(jīng)常有香客問我,道長硕舆,這世上最難降的妖魔是什么秽荞? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮抚官,結(jié)果婚禮上扬跋,老公的妹妹穿的比我還像新娘。我一直安慰自己凌节,他們只是感情好钦听,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著倍奢,像睡著了一般彪见。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娱挨,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天余指,我揣著相機(jī)與錄音,去河邊找鬼。 笑死酵镜,一個(gè)胖子當(dāng)著我的面吹牛碉碉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淮韭,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼垢粮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了靠粪?” 一聲冷哼從身側(cè)響起蜡吧,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎占键,沒想到半個(gè)月后昔善,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畔乙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年君仆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牲距。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡返咱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出牍鞠,到底是詐尸還是另有隱情咖摹,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布难述,位于F島的核電站萤晴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏龄广。R本人自食惡果不足惜硫眯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一蕴侧、第九天 我趴在偏房一處隱蔽的房頂上張望择同。 院中可真熱鬧,春花似錦净宵、人聲如沸敲才。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽紧武。三九已至,卻和暖如春敏储,著一層夾襖步出監(jiān)牢的瞬間阻星,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妥箕,地道東北人滥酥。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像畦幢,于是被迫代替她去往敵國和親坎吻。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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