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

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容