2019-07-15

```

//

//  UILabel+YBAttributeTextTapAction.m

//

//  Created by LYB on 16/7/1.

//  Copyright © 2016年 LYB. All rights reserved.

//

#import "UILabel+YBAttributeTextTapAction.h"

#import

#import

#import

@interfaceYBAttributeModel :NSObject

@property (nonatomic, copy) NSString *str;

@property (nonatomic) NSRange range;

@end

@implementationYBAttributeModel

@end

@implementationUILabel (YBAttributeTextTapAction)

#pragma mark - AssociatedObjects

- (NSMutableArray*)attributeStrings

{

    return objc_getAssociatedObject(self, _cmd);

}

- (void)setAttributeStrings:(NSMutableArray*)attributeStrings

{

    objc_setAssociatedObject(self, @selector(attributeStrings), attributeStrings, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (NSMutableDictionary *)effectDic

{

    return objc_getAssociatedObject(self, _cmd);

}

- (void)setEffectDic:(NSMutableDictionary*)effectDic

{

    objc_setAssociatedObject(self, @selector(effectDic), effectDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (BOOL)isTapAction

{

    return [objc_getAssociatedObject(self, _cmd) boolValue];

}

- (void)setIsTapAction:(BOOL)isTapAction

{

    objc_setAssociatedObject(self,@selector(isTapAction),@(isTapAction), OBJC_ASSOCIATION_ASSIGN);

}

- (void(^)(UILabel *, NSString *, NSRange, NSInteger))tapBlock

{

    returnobjc_getAssociatedObject(self,_cmd);

}

- (void)setTapBlock:(void(^)(UILabel *, NSString *, NSRange, NSInteger))tapBlock

{

    objc_setAssociatedObject(self,@selector(tapBlock), tapBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);

}

- (id)delegate

{

    returnobjc_getAssociatedObject(self,_cmd);

}

- (void)setDelegate:(id)delegate

{

    objc_setAssociatedObject(self,@selector(delegate), delegate, OBJC_ASSOCIATION_ASSIGN);

}

- (BOOL)enabledTapEffect

{

    return[objc_getAssociatedObject(self,_cmd) boolValue];

}

- (void)setEnabledTapEffect:(BOOL)enabledTapEffect

{

    objc_setAssociatedObject(self,@selector(enabledTapEffect),@(enabledTapEffect), OBJC_ASSOCIATION_ASSIGN);

    self.isTapEffect = enabledTapEffect;

}

- (BOOL)enlargeTapArea

{

    NSNumber * number = objc_getAssociatedObject(self,_cmd);

    if(!number) {

        number =@(YES);

        objc_setAssociatedObject(self,_cmd, number, OBJC_ASSOCIATION_ASSIGN);

    }

    return[number boolValue];

}

- (void)setEnlargeTapArea:(BOOL)enlargeTapArea

{

    objc_setAssociatedObject(self,@selector(enlargeTapArea),@(enlargeTapArea), OBJC_ASSOCIATION_ASSIGN);

}

- (UIColor *)tapHighlightedColor

{

    UIColor * color = objc_getAssociatedObject(self,_cmd);

    if(!color) {

        color = [UIColor lightGrayColor];

        objc_setAssociatedObject(self,_cmd, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    }

    returncolor;

}

- (void)setTapHighlightedColor:(UIColor *)tapHighlightedColor

{

    objc_setAssociatedObject(self,@selector(tapHighlightedColor), tapHighlightedColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (BOOL)isTapEffect

{

    return[objc_getAssociatedObject(self,_cmd) boolValue];

}

- (void)setIsTapEffect:(BOOL)isTapEffect

{

    objc_setAssociatedObject(self,@selector(isTapEffect),@(isTapEffect), OBJC_ASSOCIATION_ASSIGN);

}

#pragma mark - mainFunction

- (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings tapClicked:(void(^) (UILabel * label, NSString *string, NSRange range, NSInteger index))tapClick

{

    [selfyb_removeAttributeTapActions];

    [selfyb_getRangesWithStrings:strings];

    self.userInteractionEnabled =YES;


    if(self.tapBlock != tapClick) {

        self.tapBlock = tapClick;

    }

}

- (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings

                                   delegate:(id )delegate

{

    [selfyb_removeAttributeTapActions];

    [selfyb_getRangesWithStrings:strings];

    self.userInteractionEnabled =YES;


    if(self.delegate != delegate) {

        self.delegate = delegate;

    }

}

- (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges tapClicked:(void(^)(UILabel *, NSString *, NSRange, NSInteger))tapClick

{

    [selfyb_removeAttributeTapActions];

    [selfyb_getRangesWithRanges:ranges];

    self.userInteractionEnabled =YES;


    if(self.tapBlock != tapClick) {

        self.tapBlock = tapClick;

    }

}

- (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges delegate:(id)delegate

{

    [selfyb_removeAttributeTapActions];

    [selfyb_getRangesWithRanges:ranges];

    self.userInteractionEnabled =YES;


    if(self.delegate != delegate) {

        self.delegate = delegate;

    }

}

- (void)yb_removeAttributeTapActions

{

    self.tapBlock =nil;

    self.delegate =nil;

    self.effectDic =nil;

    self.isTapAction =NO;

    self.attributeStrings = [NSMutableArray array];

}

#pragma mark - touchAction

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    if(!self.isTapAction) {

        [supertouchesBegan:touches withEvent:event];

        return;

    }


    if(objc_getAssociatedObject(self,@selector(enabledTapEffect))) {

        self.isTapEffect =self.enabledTapEffect;

    }


    UITouch *touch = [touches anyObject];


    CGPoint point = [touch locationInView:self];


    __weak typeof(self) weakSelf = self;


    BOOLret = [selfyb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) {


        if(weakSelf.isTapEffect) {


            [weakSelf yb_saveEffectDicWithRange:range];


            [weakSelf yb_tapEffectWithStatus:YES];

        }


    }];

    if(!ret) {

        [supertouchesBegan:touches withEvent:event];

    }

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{

    if(!self.isTapAction) {

        [supertouchesEnded:touches withEvent:event];

        return;

    }

    if(self.isTapEffect) {

        [selfperformSelectorOnMainThread:@selector(yb_tapEffectWithStatus:) withObject:nilwaitUntilDone:NO];

    }


    UITouch *touch = [touches anyObject];


    CGPoint point = [touch locationInView:self];


    __weak typeof(self) weakSelf = self;


    BOOLret = [selfyb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) {

        if(weakSelf.tapBlock) {

            weakSelf.tapBlock (weakSelf, string, range, index);

        }


        if(weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(yb_tapAttributeInLabel:string:range:index:)]) {

            [weakSelf.delegate yb_tapAttributeInLabel:weakSelf string:string range:range index:index];

        }

    }];

    if(!ret) {

        [supertouchesEnded:touches withEvent:event];

    }

}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

{

    if(!self.isTapAction) {

        [supertouchesCancelled:touches withEvent:event];

        return;

    }

    if(self.isTapEffect) {

        [selfperformSelectorOnMainThread:@selector(yb_tapEffectWithStatus:) withObject:nilwaitUntilDone:NO];

    }

    UITouch *touch = [touches anyObject];


    CGPoint point = [touch locationInView:self];


    __weak typeof(self) weakSelf = self;


    BOOLret = [selfyb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) {

        if(weakSelf.tapBlock) {

            weakSelf.tapBlock (weakSelf, string, range, index);

        }


        if(weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(yb_tapAttributeInLabel:string:range:index:)]) {

            [weakSelf.delegate yb_tapAttributeInLabel:weakSelf string:string range:range index:index];

        }

    }];

    if(!ret) {

        [supertouchesCancelled:touches withEvent:event];

    }

}

#pragma mark - getTapFrame

- (BOOL)yb_getTapFrameWithTouchPoint:(CGPoint)point result:(void(^) (NSString *string , NSRange range , NSInteger index))resultBlock

{

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attributedText);


    CGMutablePathRef Path = CGPathCreateMutable();


    CGPathAddRect(Path,NULL, CGRectMake(0,0,self.bounds.size.width,self.bounds.size.height +20));


    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0), Path,NULL);


    CFArrayRef lines = CTFrameGetLines(frame);


    CGFloat total_height =  [selfyb_textSizeWithAttributedString:self.attributedText width:self.bounds.size.width numberOfLines:0].height;


    if(!lines) {

        CFRelease(frame);

        CFRelease(framesetter);

        CGPathRelease(Path);

        returnNO;

    }


    CFIndex count = CFArrayGetCount(lines);


    CGPoint origins[count];


    CTFrameGetLineOrigins(frame, CFRangeMake(0,0), origins);


    CGAffineTransform transform = [selfyb_transformForCoreText];


    for(CFIndex i =0; i < count; i++) {

        CGPoint linePoint = origins[i];


        CTLineRef line = CFArrayGetValueAtIndex(lines, i);


        CGRect flippedRect = [selfyb_getLineBounds:line point:linePoint];


        CGRect rect = CGRectApplyAffineTransform(flippedRect, transform);


        CGFloat lineOutSpace = (self.bounds.size.height - total_height) /2;


        rect.origin.y = lineOutSpace + [selfyb_getLineOrign:line];


        if(self.enlargeTapArea) {

            rect.origin.y -=5;

            rect.size.height +=10;

        }


        if(CGRectContainsPoint(rect, point)) {


            CGPoint relativePoint = CGPointMake(point.x - CGRectGetMinX(rect), point.y - CGRectGetMinY(rect));

            CFIndex index = CTLineGetStringIndexForPosition(line, relativePoint);


            CGFloat offset;


            CTLineGetOffsetForStringIndex(line, index, &offset);


            if(offset > relativePoint.x) {

                index = index -1;

            }


            NSInteger link_count =self.attributeStrings.count;


            for(intj =0; j < link_count; j++) {


                YBAttributeModel *model =self.attributeStrings[j];


                NSRange link_range = model.range;

                if(NSLocationInRange(index, link_range)) {

                    if(resultBlock) {

                        resultBlock (model.str , model.range , (NSInteger)j);

                    }

                    CFRelease(frame);

                    CFRelease(framesetter);

                    CGPathRelease(Path);

                    returnYES;

                }

            }

        }

    }

    CFRelease(frame);

    CFRelease(framesetter);

    CGPathRelease(Path);

    return NO;

}

- (CGAffineTransform)yb_transformForCoreText

{

    returnCGAffineTransformScale(CGAffineTransformMakeTranslation(0,self.bounds.size.height),1.f, -1.f);

}

- (CGRect)yb_getLineBounds:(CTLineRef)line point:(CGPoint)point

{

    CGFloat ascent =0.0f;

    CGFloat descent =0.0f;

    CGFloat leading =0.0f;

    CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading);

    CGFloat height =0.0f;


    CFRange range = CTLineGetStringRange(line);

    NSAttributedString * attributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(range.location, range.length)];

    if([attributedString.string hasSuffix:@"\n"] && attributedString.string.length >1) {

        attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, attributedString.length -1)];

    }

    height = [selfyb_textSizeWithAttributedString:attributedString width:self.bounds.size.width numberOfLines:0].height;

    returnCGRectMake(point.x, point.y , width, height);

}

- (CGFloat)yb_getLineOrign:(CTLineRef)line

{

    CFRange range = CTLineGetStringRange(line);

    if(range.location ==0) {

        return0.;

    }else{

        NSAttributedString * attributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(0, range.location)];

        if([attributedString.string hasSuffix:@"\n"] && attributedString.string.length >1) {

            attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, attributedString.length -1)];

        }

        return[selfyb_textSizeWithAttributedString:attributedString width:self.bounds.size.width numberOfLines:0].height;

    }

}

- (CGSize)yb_textSizeWithAttributedString:(NSAttributedString *)attributedString width:(float)width numberOfLines:(NSInteger)numberOfLines

{

    @autoreleasepool {

        UILabel *sizeLabel = [[UILabel alloc] initWithFrame:CGRectZero];

        sizeLabel.numberOfLines = numberOfLines;

        sizeLabel.attributedText = attributedString;

        CGSize fitSize = [sizeLabel sizeThatFits:CGSizeMake(width, MAXFLOAT)];

        returnfitSize;

    }

}

#pragma mark - tapEffect

- (void)yb_tapEffectWithStatus:(BOOL)status

{

    if(self.isTapEffect) {

        NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];


        NSMutableAttributedString *subAtt = [[NSMutableAttributedString alloc] initWithAttributedString:[[self.effectDic allValues] firstObject]];


        NSRange range = NSRangeFromString([[self.effectDic allKeys] firstObject]);


        if(status) {

            [subAtt addAttribute:NSBackgroundColorAttributeName value:self.tapHighlightedColor range:NSMakeRange(0, subAtt.string.length)];


            [attStr replaceCharactersInRange:range withAttributedString:subAtt];

        }else{


            [attStr replaceCharactersInRange:range withAttributedString:subAtt];

        }

        self.attributedText = attStr;

    }

}

- (void)yb_saveEffectDicWithRange:(NSRange)range

{

    self.effectDic = [NSMutableDictionary dictionary];


    NSAttributedString *subAttribute = [self.attributedText attributedSubstringFromRange:range];


    [self.effectDic setObject:subAttribute forKey:NSStringFromRange(range)];

}

#pragma mark - getRange

- (void)yb_getRangesWithStrings:(NSArray   *)strings

{

    if(self.attributedText ==nil) {

        self.isTapAction =NO;

        return;

    }

    self.isTapAction =YES;

    self.isTapEffect =YES;

    __block  NSString *totalStr =self.attributedText.string;

    self.attributeStrings = [NSMutableArray array];

    __weak typeof(self) weakSelf = self;


    [strings enumerateObjectsUsingBlock:^(NSString *_Nonnullobj, NSUInteger idx,BOOL*_Nonnullstop) {


        NSRange range = [totalStr rangeOfString:obj];


        if(range.length !=0) {


            totalStr = [totalStr stringByReplacingCharactersInRange:range withString:[weakSelf yb_getStringWithRange:range]];


            YBAttributeModel *model = [YBAttributeModel new];

            model.range = range;

            model.str = obj;

            [weakSelf.attributeStrings addObject:model];


        }


    }];

}

- (void)yb_getRangesWithRanges:(NSArray   *)ranges

{

    if(self.attributedText ==nil) {

        self.isTapAction =NO;

        return;

    }


    self.isTapAction =YES;

    self.isTapEffect =YES;

    __block  NSString *totalStr =self.attributedText.string;

    self.attributeStrings = [NSMutableArray array];

    __weak typeof(self) weakSelf = self;


    [ranges enumerateObjectsUsingBlock:^(NSString *_Nonnullobj, NSUInteger idx,BOOL*_Nonnullstop) {

        NSRange range = NSRangeFromString(obj);

        NSAssert(totalStr.length >= range.location + range.length,@"NSRange(%ld,%ld) is out of bounds",range.location,range.length);

        NSString * string = [totalStr substringWithRange:range];


        YBAttributeModel *model = [YBAttributeModel new];

        model.range = range;

        model.str = string;

        [weakSelf.attributeStrings addObject:model];

    }];

}

- (NSString *)yb_getStringWithRange:(NSRange)range

{

    NSMutableString *string = [NSMutableString string];


    for(inti =0; i < range.length ; i++) {


        [string appendString:@" "];

    }

    returnstring;

}

#pragma mark - KVO

- (void)yb_addObserver

{

    [selfaddObserver:selfforKeyPath:@"attributedText"options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];

}

- (void)yb_removeObserver

{

    idinfo =self.observationInfo;

    NSString * key =@"attributedText";

    NSArray *array = [info valueForKey:@"_observances"];

    for(idobjcinarray) {

        idProperties = [objc valueForKeyPath:@"_property"];

        NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];

        if([key isEqualToString:keyPath]) {

            [selfremoveObserver:selfforKeyPath:@"attributedText"context:nil];

        }

    }

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void*)context

{

    if([keyPath isEqualToString:@"attributedText"]) {

        if(self.isTapAction) {

            if(![change[NSKeyValueChangeNewKey] isEqual: change[NSKeyValueChangeOldKey]]) {


            }

        }

    }

}

@end

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容