美文网首页
iOS 融云消息自定义[a](b)变成a高亮 类似于MD语法

iOS 融云消息自定义[a](b)变成a高亮 类似于MD语法

作者: 何康老鬼 | 来源:发表于2022-06-07 18:06 被阅读0次

    公司需求要把后台设置的【内容】(具体链接)样式变成 内容 高亮显示,最终效果如下:

    截屏2022-06-07 下午5.55.35.png

    后台给的数据结构

    默认支持以下3种协议
    http http://qq.com 
    email email@qq.com
    phone 18870912166
    
    v723开始支持
    1.yikao协议
    yikao://member/?id=28686130
    2.md语法格式超链解析
    [链接](以上4种协议)
    [http](http://qq.com) 
    
    [email](email@qq.com)
    
    [phone](18870912166)
    
    [yikao](yikao://member/?id=28686130)
    

    需要实现的效果

    默认支持以下3种协议
    http http://qq.com 
    email email@qq.com
    phone 18870912166
    
    v723开始支持
    1.yikao协议
    yikao://member/?id=28686130
    2.md语法格式超链解析
    [链接](以上4种协议)
    http
    
    email
    
    phone
    
    yikao
    

    实现代码

    -(void)creatUI
    {
       [self.view addSubview:self.textLabel];
        self.textLabel.text = @"默认支持以下3种协议\nhttp http://qq.com \nemail email@qq.com\nphone 18870912166\n\nv723开始支持\n1.yikao协议\nyikao://member/?id=28686130\n2.md语法格式超链解析\n[链接](以上4种协议)\n[http](http://qq.com) \n\n[email](email@qq.com)\n\n[phone](18870912166)\n\n[yikao](yikao://member/?id=28686130)";
        self.textLabel.frame =  CGRectMake(10, 40,220, 300);
    }
    
    - (OSAttributedLabel *)textLabel{
        if (!_textLabel) {
            _textLabel = [[OSAttributedLabel alloc] initWithFrame:CGRectZero];
            _textLabel.font = [UIFont systemFontOfSize:14];
            _textLabel.numberOfLines = 0;
            [_textLabel setLineBreakMode:NSLineBreakByWordWrapping];
            [_textLabel setTextAlignment:NSTextAlignmentLeft];
            _textLabel.delegate = self;
            _textLabel.textCheckingTypes = NSTextCheckingTypePhoneNumber|NSTextCheckingTypeLink;
            _textLabel.backgroundColor = UIColor.systemPinkColor;
        }
        return _textLabel;
    }
    
    

    用到的两个类.h

    //
    //  OSAttributedLabel.h
    //  YiKao
    //
    //  Created by John.lee on 2022/3/24.
    //  Copyright © 2022 YiKao. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    /**
     *  RCAttributedDataSource
     */
    @protocol OSAttributedDataSource <NSObject>
    /**
     *  attributeDictionaryForTextType
     *
     *  @param textType textType
     *
     *  @return return NSDictionary
     */
    - (NSDictionary *)attributeDictionaryForTextType:(NSTextCheckingTypes)textType;
    /**
     *  highlightedAttributeDictionaryForTextType
     *
     *  @param textType textType
     *
     *  @return NSDictionary
     */
    - (NSDictionary *)highlightedAttributeDictionaryForTextType:(NSTextCheckingType)textType;
    
    @end
    
    @protocol OSAttributedLabelDelegate;
    
    /**
     *  Override UILabel @property to accept both NSString and NSAttributedString
     */
    @protocol OSAttributedLabel <NSObject>
    
    /**
     *  text
     */
    @property (nonatomic, copy) id text;
    
    @end
    @interface OSAttributedLabel : UILabel<OSAttributedDataSource, UIGestureRecognizerDelegate>
    /**
     * 可以通过设置attributeDataSource或者attributeDictionary、highlightedAttributeDictionary来自定义不同文本的字体颜色
     */
    @property (nonatomic, strong) id<OSAttributedDataSource> attributeDataSource;
    /**
     * 可以通过设置attributedStrings可以给一些字符添加点击事件等,例如在实现的会话列表里修改文本消息内容
     *  -(void)willDisplayConversationTableCell:(RCMessageBaseCell *)cell atIndexPath:(NSIndexPath *)indexPath{
     *
     *   if ([cell isKindOfClass:[RCTextMessageCell class]]) {
     *      RCTextMessageCell *newCell = (RCTextMessageCell *)cell;
     *      if (newCell.textLabel.text.length>3) {
     *          NSTextCheckingResult *textCheckingResult = [NSTextCheckingResult linkCheckingResultWithRange:(NSMakeRange(0,
     *3)) URL:[NSURL URLWithString:@"http://www.baidu.com"]]; [newCell.textLabel.attributedStrings
     *addObject:textCheckingResult]; [newCell.textLabel setTextHighlighted:YES atPoint:CGPointMake(0, 3)];
     *       }
     *    }
     *}
     *
     */
    @property (nonatomic, strong) NSMutableArray *attributedStrings;
    /*!
     点击回调
     */
    @property (nonatomic, weak) id<OSAttributedLabelDelegate> delegate;
    /**
     *  attributeDictionary
     */
    @property (nonatomic, strong) NSDictionary *attributeDictionary;
    /**
     *  highlightedAttributeDictionary
     */
    @property (nonatomic, strong) NSDictionary *highlightedAttributeDictionary;
    /**
     *  NSTextCheckingTypes 格式类型
     */
    @property (nonatomic, assign) NSTextCheckingTypes textCheckingTypes;
    /**
     *  NSTextCheckingTypes current格式类型
     */
    @property (nonatomic, readonly, assign) NSTextCheckingType currentTextCheckingType;
    /**
     *  setTextdataDetectorEnabled
     *
     *  @param text                text
     *  @param dataDetectorEnabled dataDetectorEnabled
     */
    - (void)setText:(NSString *)text dataDetectorEnabled:(BOOL)dataDetectorEnabled;
    ///**
    // *  textInfoAtPoint
    // *
    // *  @param point point
    // *
    // *  @return RCAttributedLabelClickedTextInfo
    // */
    //- (RCAttributedLabelClickedTextInfo *)textInfoAtPoint:(CGPoint)point;
    /**
     *  setTextHighlighted
     *
     *  @param highlighted highlighted
     *  @param point       point
     */
    - (void)setTextHighlighted:(BOOL)highlighted atPoint:(CGPoint)point;
    
    
    @end
    
    
    /*!
     RCAttributedLabel点击回调
     */
    @protocol OSAttributedLabelDelegate <NSObject>
    @optional
    
    /*!
     点击URL的回调
    
     @param label 当前Label
     @param url   点击的URL
     */
    - (void)attributedLabel:(OSAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url;
    
    /*!
     点击电话号码的回调
    
     @param label       当前Label
     @param phoneNumber 点击的URL
     */
    - (void)attributedLabel:(OSAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber;
    
    /*!
     点击Label的回调
    
     @param label   当前Label
     @param content 点击的内容
     */
    - (void)attributedLabel:(OSAttributedLabel *)label didTapLabel:(NSString *)content;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    用到的两个类.m

    //
    //  OSAttributedLabel.m
    //  YiKao
    //
    //  Created by John.lee on 2022/3/24.
    //  Copyright © 2022 YiKao. All rights reserved.
    //
    
    #import "OSAttributedLabel.h"
    #import <CoreText/CoreText.h>
    #import "NSString+Pinyin.h" //判断是不是手机号的 报错的话替换一下自己用的工具类
    #pragma mark - System Version
    @interface OSAttributedLabel ()
    
    @property (nonatomic, copy) NSString *originalString;
    @property (nonatomic, assign) BOOL dataDetectorEnabled;
    @property (nonatomic, assign) BOOL needGenerateAttributed;
    @property (nonatomic, assign) NSRange rangeOfTextHighlighted;
    @property (nonatomic, strong) UITapGestureRecognizer *tapGestureRecognizer;
    
    @end
    
    @implementation OSAttributedLabel
    
    - (void)layoutSubviews {
        [self generateAttributed];
        [super layoutSubviews];
    }
    
    #pragma mark - Public Methods
    
    - (void)setTextHighlighted:(BOOL)highlighted atPoint:(CGPoint)point {
        if (highlighted == NO) {
            self.rangeOfTextHighlighted = NSMakeRange(0, 0);
        } else {
            self.rangeOfTextHighlighted = [self textRangeAtPoint:point];
        }
        [self generateAttributedString];
    }
    
    #pragma mark - RCAttributedDataSource
    - (NSDictionary *)attributeDictionaryForTextType:(NSTextCheckingTypes)textType {
        if (self.attributeDictionary) {
            NSNumber *textCheckingTypesNumber = [NSNumber numberWithUnsignedLongLong:textType];
            return [self.attributeDictionary objectForKey:textCheckingTypesNumber];
        }
        if (self.attributeDataSource) {
            return [self.attributeDataSource attributeDictionaryForTextType:textType];
        }
        switch (textType) {
        case NSTextCheckingTypePhoneNumber: {
            _currentTextCheckingType = NSTextCheckingTypePhoneNumber;
            return @{
                NSForegroundColorAttributeName :
                    [UIColor blueColor],
                NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle),
                NSUnderlineColorAttributeName : [UIColor yellowColor]
            };
        }
            case NSTextCheckingTypeLink: {
            _currentTextCheckingType = NSTextCheckingTypeLink;
            return @{
                NSForegroundColorAttributeName :
                    [UIColor blueColor],
                NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle),
                NSUnderlineColorAttributeName :
                    [UIColor blueColor]
            };
        }
            case NSTextCheckingTypeRegularExpression: {
            _currentTextCheckingType = NSTextCheckingTypeRegularExpression;
            return @{
                NSForegroundColorAttributeName :
                    [UIColor blueColor],
                NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle),
                NSUnderlineColorAttributeName :
                    [UIColor blueColor]
            };
        }
        default:
            break;
        }
        return nil;
    }
    
    - (NSDictionary *)highlightedAttributeDictionaryForTextType:(NSTextCheckingType)textType {
        if (self.attributeDictionary) {
            NSNumber *textCheckingTypesNumber = [NSNumber numberWithUnsignedLongLong:textType];
            return [self.attributeDictionary objectForKey:textCheckingTypesNumber];
        }
        if (self.attributeDataSource) {
            return [self.attributeDataSource highlightedAttributeDictionaryForTextType:textType];
        }
        switch (textType) {
        case NSTextCheckingTypePhoneNumber: {
            _currentTextCheckingType = NSTextCheckingTypePhoneNumber;
              return @{
                    NSForegroundColorAttributeName : [UIColor yellowColor],
                    NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle),
                    NSUnderlineColorAttributeName : [UIColor yellowColor]
                };
        }
        case NSTextCheckingTypeLink: {
            _currentTextCheckingType = NSTextCheckingTypeLink;
            
                return @{
                    NSForegroundColorAttributeName : [UIColor greenColor],
                    NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle),
                    NSUnderlineColorAttributeName : [UIColor greenColor]
                };
        }
        case NSTextCheckingTypeRegularExpression: {
                _currentTextCheckingType = NSTextCheckingTypeRegularExpression;
                    return @{
                        NSForegroundColorAttributeName : [UIColor greenColor],
                        NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle),
                        NSUnderlineColorAttributeName : [UIColor greenColor]
                    };
            }
        default:
            break;
        }
        return nil;
    }
    
    #pragma mark - UIGestureRecognizer
    - (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
        if ([gestureRecognizer state] != UIGestureRecognizerStateEnded) {
            return;
        }
        NSTextCheckingResult *result = [self linkAtPoint:[gestureRecognizer locationInView:self]];
        if (!result) {
            if (self.delegate != nil) {
                if ([self.delegate respondsToSelector:@selector(attributedLabel:didTapLabel:)]) {
                    [self.delegate attributedLabel:self didTapLabel:self.originalString];
                }
            }
            return;
        }
    
        switch (result.resultType) {
            case NSTextCheckingTypeLink: case NSTextCheckingTypeRegularExpression:            NSLog(@"result---------%@",result.URL);
            break;
        case NSTextCheckingTypePhoneNumber:
                NSLog(@"result---------%@",result.phoneNumber);
            break;
        default:
            break;
        }
    }
    
    #pragma mark - Private Methods
    - (NSRange)textRangeAtPoint:(CGPoint)point {
        if (self.dataDetectorEnabled == NO) {
            return NSMakeRange(0, 0);
        }
        CFIndex charIndex = [self characterIndexAtPoint:point];
        for (NSTextCheckingResult *textCheckingResult in self.attributedStrings) {
            for (int i = 0; i < textCheckingResult.numberOfRanges; i++) {
                NSRange range = [textCheckingResult rangeAtIndex:i];
                if (NSLocationInRange(charIndex, range)) {
                    return range;
                }
            }
        }
        return NSMakeRange(0, 0);
    }
    
    - (NSTextCheckingResult *)linkAtCharacterIndex:(CFIndex)idx {
    //    NSLog(@"idx------------%ld",idx);
        NSMutableAttributedString *attributedString =
            [[NSMutableAttributedString alloc] initWithString:self.originalString];
        for (NSTextCheckingResult *result in self.attributedStrings) {
            NSRange range = result.range;
            NSString *str = [self.originalString substringWithRange:result.range];
    //        NSLog(@"range-------%ld----%ld---%@",range.location,range.length,str);
            if ([str hasPrefix:@"["]) {
                NSString *b = [self matchString:str toRegexString:@"(\\[(.*?)\\])"].firstObject;
                b = [b stringByReplacingOccurrencesOfString:@"[" withString:@""];
                b = [b stringByReplacingOccurrencesOfString:@"]" withString:@""];
                b = [NSString  stringWithFormat:@"%@\n",b];
    
                NSAttributedString *endString = [[NSAttributedString alloc]initWithString:b attributes:@{}];
    //           NSLog(@"endString------%@",endString);
                //这里rang 范围会改变 所以需要重新求一下范围
                NSRange oldRang = [attributedString.mutableString rangeOfString:str];
                [attributedString deleteCharactersInRange:oldRang];
                [attributedString insertAttributedString:endString atIndex:oldRang.location];
                NSRange newRang = NSMakeRange(oldRang.location, b.length);
                range = newRang;
                if ((CFIndex)range.location <= idx && idx <= (CFIndex)(range.location + range.length)) {
                    return result;
                }
            }else{
                if ((CFIndex)range.location <= idx && idx <= (CFIndex)(range.location + range.length)) {
                    return result;
                }
            }
    
    
        }
    
        return nil;
    }
    - (void)generateAttributed {
        if (self.dataDetectorEnabled && self.needGenerateAttributed) {
            self.needGenerateAttributed = NO;
            [self generateAttributedStrings];
            [self generateAttributedString];
        }
    }
    
    - (void)generateAttributedStrings {
        if (!self.originalString) {
            return;
        }
        NSError *error = nil;
          
        NSDataDetector *dataDetector = [[NSDataDetector alloc] initWithTypes:self.textCheckingTypes error:&error];
        if (error != nil) {
          
            [super setText:self.originalString];
            return;
        }
        self.attributedStrings = [NSMutableArray array];
        
    //    NSString *regulaStr = @"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)";
    //    NSRegularExpression *pattern = [NSRegularExpression regularExpressionWithPattern:regulaStr options:NSRegularExpressionAnchorsMatchLines error:nil];
        
        NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:@"(\\[(.*?)\\])(\\(.*?\\))" options:0 error:nil];
        
        __weak typeof(self) weakSelf = self;
        //文本少于 500 同步计算高亮结果,大于 500 异步计算
        if(self.originalString.length < 500) {
            [dataDetector enumerateMatchesInString:self.originalString
               options:kNilOptions
                 range:NSMakeRange(0, self.originalString.length)
            usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
                __strong typeof(weakSelf) strongSelf = weakSelf;
                //tip:处理筛选掉不需要和下面重复高亮的链接 否则点击响应事件会错乱
                NSString * str;
                if (strongSelf.originalString.length == result.range.length) {
                    str = [strongSelf.originalString substringWithRange:result.range];
                }else{
                    str = [strongSelf.originalString substringWithRange:NSMakeRange(result.range.location-1, result.range.length+1)];
                }
                if ([str hasPrefix:@"("]) {
                }else{
                    strongSelf->_currentTextCheckingType = result.resultType;
                    [strongSelf.attributedStrings addObject:result];
                }
            }];
    //        [pattern enumerateMatchesInString:self.originalString options:kNilOptions range:NSMakeRange(0, self.originalString.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
    //            __strong typeof(weakSelf) strongSelf = weakSelf;
    //            strongSelf->_currentTextCheckingType = result.resultType;
    //            [strongSelf.attributedStrings addObject:result];
    //        }];
            
            [regularExpression enumerateMatchesInString:self.originalString options:kNilOptions range:NSMakeRange(0, self.originalString.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
                __strong typeof(weakSelf) strongSelf = weakSelf;
                NSString * str = [strongSelf.originalString substringWithRange:result.range];
    //            NSLog(@"str----------%@",str);
                //取出名称
                NSString *b = [strongSelf matchString:str toRegexString:@"(\\[(.*?)\\])"].firstObject;
                 b = [b stringByReplacingOccurrencesOfString:@"[" withString:@""];
                 b = [b stringByReplacingOccurrencesOfString:@"]" withString:@""];
    //           b = [NSString  stringWithFormat:@"%@ \n",b];
                //取出链接
                NSString *c = [strongSelf matchString:str toRegexString:@"(\\((.*?)\\))"].firstObject;
                c = [c stringByReplacingOccurrencesOfString:@"(" withString:@""];
                c = [c stringByReplacingOccurrencesOfString:@")" withString:@""];
    
                //判断是否包含 邮箱、电话、yikao、http
                if ([NSString isValidEmail:c]||[c hasPrefix:@"yikao://"]||[NSString isValidPhone:c]||[c hasPrefix:@"http"]) {
                    NSTextCheckingResult *textCheckingResult = [NSTextCheckingResult linkCheckingResultWithRange:(result.range) URL:[NSURL URLWithString:c]];
                    strongSelf->_currentTextCheckingType = textCheckingResult.resultType;
                    [strongSelf.attributedStrings addObject:textCheckingResult];
                }
            }];
            
            
            
        }else {
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                [dataDetector enumerateMatchesInString:self.originalString
                   options:kNilOptions
                     range:NSMakeRange(0, self.originalString.length)
                usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        __strong typeof(weakSelf) strongSelf = weakSelf;
                        //tip:处理筛选掉不需要和下面重复高亮的链接 否则点击响应事件会错乱
                        NSString * str = [self.originalString substringWithRange:result.range];
                        if ([str hasPrefix:@"("]) {
                        }else{
                            strongSelf->_currentTextCheckingType = result.resultType;
                            [strongSelf.attributedStrings addObject:result];
                        }
                        
                    });
                }];
    //            [pattern enumerateMatchesInString:self.originalString options:kNilOptions range:NSMakeRange(0, self.originalString.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
    //                __strong typeof(weakSelf) strongSelf = weakSelf;
    //                strongSelf->_currentTextCheckingType = result.resultType;
    //                [strongSelf.attributedStrings addObject:result];
    //            }];
                
                [regularExpression enumerateMatchesInString:self.originalString options:kNilOptions range:NSMakeRange(0, self.originalString.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
                    __strong typeof(weakSelf) strongSelf = weakSelf;
                    NSString * str = [self.originalString substringWithRange:result.range];
    //                NSLog(@"str----------%@",str);
                    //取出名称
                    NSString *b = [self matchString:str toRegexString:@"(\\[(.*?)\\])"].firstObject;
                     b = [b stringByReplacingOccurrencesOfString:@"[" withString:@""];
                     b = [b stringByReplacingOccurrencesOfString:@"]" withString:@""];
        //           b = [NSString  stringWithFormat:@"%@ \n",b];
                    //取出链接
                    NSString *c = [self matchString:str toRegexString:@"(\\((.*?)\\))"].firstObject;
                    c = [c stringByReplacingOccurrencesOfString:@"(" withString:@""];
                    c = [c stringByReplacingOccurrencesOfString:@")" withString:@""];
    
                    //判断是否包含 邮箱、电话、yikao、http
                    if ([NSString isValidEmail:c]||[c hasPrefix:@"yikao://"]||[NSString isValidPhone:c]||[c hasPrefix:@"http"]) {
                        NSTextCheckingResult *textCheckingResult = [NSTextCheckingResult linkCheckingResultWithRange:(result.range) URL:[NSURL URLWithString:c]];
                        strongSelf->_currentTextCheckingType = textCheckingResult.resultType;
                        [strongSelf.attributedStrings addObject:textCheckingResult];
                    }
                }];
            });
        }
    }
    
    - (void)generateAttributedString {
        if (!self.originalString) {
            return;
        }
        NSMutableAttributedString *attributedString =
            [[NSMutableAttributedString alloc] initWithString:self.originalString];
        for (NSTextCheckingResult *textCheckingResult in self.attributedStrings) {
            for (int i = 0; i < textCheckingResult.numberOfRanges; i++) {
                NSRange range = [textCheckingResult rangeAtIndex:i];
                NSString *str = [self.originalString substringWithRange:textCheckingResult.range];
                NSLog(@"str2------------%@",str);
                NSDictionary *attributeDictionary;
                if ([str hasPrefix:@"["]) {
                    NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:@"(\\(.*?\\))" options:0 error:nil];
                    NSArray *matches = [regularExpression matchesInString:str options:0 range:NSMakeRange(0, str.length)];
                    NSString *newStr = [str substringWithRange:[matches.firstObject range] ];
                    newStr = [str stringByReplacingOccurrencesOfString:@"(" withString:@""];
                    newStr = [str stringByReplacingOccurrencesOfString:@")" withString:@""];
                    if ([NSString isValidPhone:newStr]) {
                        attributeDictionary = [self attributeDictionaryForTextType:NSTextCheckingTypePhoneNumber];
                    }else{
                        attributeDictionary = [self attributeDictionaryForTextType:NSTextCheckingTypeLink];
                    }
                }else{
                    attributeDictionary = [self attributeDictionaryForTextType:textCheckingResult.resultType];
                }
                if (NSEqualRanges(range, self.rangeOfTextHighlighted))
                attributeDictionary = [self highlightedAttributeDictionaryForTextType:textCheckingResult.resultType];
                if (attributeDictionary) {
                    if (self.originalString.length >= (range.location + range.length)) {
                        //取出名称
                        if ([str hasPrefix:@"["]) {
                            NSString *b = [self matchString:str toRegexString:@"(\\[(.*?)\\])"].firstObject;
                            b = [b stringByReplacingOccurrencesOfString:@"[" withString:@""];
                            b = [b stringByReplacingOccurrencesOfString:@"]" withString:@""];
    //                      b = [NSString  stringWithFormat:@"%@\n",b];
    
                           NSAttributedString *endString = [[NSAttributedString alloc]initWithString:b attributes:attributeDictionary];
                           NSLog(@"endString------%@",endString);
                            //这里rang 范围会改变 所以需要重新求一下范围
                            NSRange newRang = [attributedString.mutableString rangeOfString:str];
                            [attributedString deleteCharactersInRange:newRang];
                            [attributedString insertAttributedString:endString atIndex:newRang.location];
                            
                        }else{
                           
                           NSAttributedString *subString =
                               [[NSAttributedString alloc] initWithString:[self.originalString substringWithRange:range]
                                                               attributes:attributeDictionary];
                         
                           NSLog(@"subString------%@",subString);
                           
                           [attributedString replaceCharactersInRange:range withAttributedString:subString];
                        }
                         
                    }
                }
            }
        }
        self.attributedText = attributedString;
    }
    
    - (NSUInteger)characterIndexAtPoint:(CGPoint)p {
        if (!CGRectContainsPoint(self.bounds, p)) {
            return NSNotFound;
        }
        //(11,129)
        p = CGPointMake(p.x - self.bounds.origin.x, self.bounds.size.height - p.y);
    
        NSMutableAttributedString *optimizedAttributedText = [self.attributedText mutableCopy];
        /**
            这里在结尾为 "\n" 的字符串后加 "\n" ,是因为 CTFramesetterCreateWithAttributedString 计算出的字符串在
           CTFramesetterSuggestFrameSizeWithConstraints 中计算行高会少算一行,CTFramesetterCreateFrame 这个函数的结果 frame
           中可以查看到计算出多少行。
         */
        if (optimizedAttributedText.string.length > 0 && [[optimizedAttributedText.string substringFromIndex:optimizedAttributedText.string.length - 1]
                isEqualToString:@"\n"]) {
            [optimizedAttributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]];
        }
        // use label's font and lineBreakMode properties in case the attributedText does not contain such attributes
        [optimizedAttributedText
            enumerateAttributesInRange:NSMakeRange(0, [optimizedAttributedText length])
                               options:0
                            usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
                                if (!attrs[(NSString *)kCTFontAttributeName]) {
                                    [optimizedAttributedText addAttribute:(NSString *)kCTFontAttributeName
                                                                    value:self.font
                                                                    range:NSMakeRange(0, [optimizedAttributedText length])];
                                }
    
                                if (!attrs[(NSString *)kCTParagraphStyleAttributeName]) {
                                    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
                                    [paragraphStyle setLineBreakMode:self.lineBreakMode];
                                    [optimizedAttributedText addAttribute:(NSString *)kCTParagraphStyleAttributeName
                                                                    value:paragraphStyle
                                                                    range:range];
                                }
                            }];
    
        // modify kCTLineBreakByTruncatingTail lineBreakMode to NSLineBreakByWordWrapping
        [optimizedAttributedText
            enumerateAttribute:(NSString *)kCTParagraphStyleAttributeName
                       inRange:NSMakeRange(0, [optimizedAttributedText length])
                       options:0
                    usingBlock:^(id value, NSRange range, BOOL *stop) {
                        NSMutableParagraphStyle *paragraphStyle = [value mutableCopy];
                        if (paragraphStyle.lineBreakMode == NSLineBreakByTruncatingTail) {
                            [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
                        }
                        [paragraphStyle setAlignment:self.textAlignment];
                        [optimizedAttributedText removeAttribute:(NSString *)kCTParagraphStyleAttributeName range:range];
                        [optimizedAttributedText addAttribute:(NSString *)kCTParagraphStyleAttributeName
                                                        value:paragraphStyle
                                                        range:range];
                    }];
    
        CTFramesetterRef framesetter =
            CTFramesetterCreateWithAttributedString((CFAttributedStringRef)optimizedAttributedText);
    
        CGRect textRect = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsZero);
        CGSize textSize =
            CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, [optimizedAttributedText length]),
                                                         NULL, CGSizeMake(self.bounds.size.width, CGFLOAT_MAX), NULL);
        textSize = CGSizeMake(ceil(textSize.width), ceil(textSize.height));
        textRect.origin.y += floor((self.bounds.size.height - textSize.height) / 2.0f);
        textRect.size.height = textSize.height;
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, textRect);
        CTFrameRef frame =
            CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [optimizedAttributedText length]), path, NULL);
        if (frame == NULL) {
            if (framesetter != NULL) {
                CFRelease(framesetter);
            }
            if (path != NULL) {
                CFRelease(path);
            }
            return NSNotFound;
        }
    
        CFArrayRef lines = CTFrameGetLines(frame);
        NSUInteger numberOfLines = CFArrayGetCount(lines);
        if (numberOfLines == 0) {
            if (framesetter != NULL) {
                CFRelease(framesetter);
            }
            if (frame != NULL) {
                CFRelease(frame);
            }
            if (path != NULL) {
                CFRelease(path);
            }
            return NSNotFound;
        }
    
        CGPoint lineOrigins[numberOfLines];
        CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins);
    
        NSUInteger lineIndex;
        for (lineIndex = 0; lineIndex < numberOfLines; lineIndex++) {
            if (lineIndex == numberOfLines - 1) {
                break;
            } else {
                CGPoint lineOrigin = lineOrigins[lineIndex];
                if (lineOrigin.y <= p.y) {
                    break;
                }
            }
        }
    
        if (lineIndex >= numberOfLines) {
            if (framesetter != NULL) {
                CFRelease(framesetter);
            }
            if (frame != NULL) {
                CFRelease(frame);
            }
            if (path != NULL) {
                CFRelease(path);
            }
            return NSNotFound;
        }
    
        CGPoint lineOrigin = lineOrigins[lineIndex];
        CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);
        // Convert CT coordinates to line-relative coordinates
        CGPoint relativePoint = CGPointMake(p.x - lineOrigin.x, p.y - lineOrigin.y);
        CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
    
        if (framesetter != NULL) {
            CFRelease(framesetter);
        }
        if (frame != NULL) {
            CFRelease(frame);
        }
        if (path != NULL) {
            CFRelease(path);
        }
        return idx;
    }
    
    - (NSTextCheckingResult *)linkAtPoint:(CGPoint)p {
        NSLog(@"当前手势点击的坐标-------%@",NSStringFromCGPoint(p));
        CFIndex idx = [self characterIndexAtPoint:p];
        return [self linkAtCharacterIndex:idx];
    }
    
    #pragma mark - Getters and Setters
    - (void)setText:(NSString *)text {
        [self setText:text dataDetectorEnabled:YES];
    }
    
    - (void)setText:(NSString *)text dataDetectorEnabled:(BOOL)dataDetectorEnabled {
        self.dataDetectorEnabled = dataDetectorEnabled;
        if (self.dataDetectorEnabled == NO) {
            [super setText:text];
            return;
        }
        
        self.originalString = text;
        //设置内容的时候,先做一次解析,保证准确性
        [super setText:text];
        self.needGenerateAttributed = YES;
        [self generateAttributed];
    
        self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
        [self.tapGestureRecognizer setDelegate:self];
        [self addGestureRecognizer:self.tapGestureRecognizer];
        self.userInteractionEnabled = YES;
    
    }
    - (void)setAttributeDictionary:(NSDictionary *)attributeDictionary {
        _attributeDictionary = attributeDictionary;
        self.needGenerateAttributed = YES;
    }
    
    - (void)setHighlightedAttributeDictionary:(NSDictionary *)highlightedAttributeDictionary {
        _highlightedAttributeDictionary = highlightedAttributeDictionary;
        self.needGenerateAttributed = YES;
    }
    
    - (void)setAttributeDataSource:(id<OSAttributedDataSource>)attributeDataSource {
        _attributeDataSource = attributeDataSource;
        self.needGenerateAttributed = YES;
    }
    
    - (NSString *)text {
        [self generateAttributed];
        return [super text];
    }
    
    - (NSAttributedString *)attributedText {
        [self generateAttributed];
        return [super attributedText];
    }
    
    - (NSTextCheckingTypes)textCheckingTypes {
        if (_textCheckingTypes) {
            return _textCheckingTypes;
        }
        return NSTextCheckingTypePhoneNumber;
    }
    -(NSString *)getKuohaoStr:(NSString *)content
    {
        NSString *str = content;
        if (str) {
            //处理文本内容
            NSError *error;
            NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:@"(\\[(.*?)\\])(\\(.*?\\))" options:0 error:&error];
            NSArray *matches = [regularExpression matchesInString:str options:0 range:NSMakeRange(0, str.length)];
            NSMutableArray *array = [NSMutableArray array];
            for (NSTextCheckingResult *match in matches) {
                NSRange matchRange = [match range];
                NSString *a = [str substringWithRange:matchRange];
                //取出名称
                NSString *b = [self matchString:a toRegexString:@"(\\[(.*?)\\])"].firstObject;
                 b = [b stringByReplacingOccurrencesOfString:@"[" withString:@""];
                 b = [b stringByReplacingOccurrencesOfString:@"]" withString:@""];
    //             b = [NSString  stringWithFormat:@"%@ \n",b];
                //取出链接
                NSString *c = [self matchString:a toRegexString:@"(\\((.*?)\\))"].firstObject;
                c = [c stringByReplacingOccurrencesOfString:@"(" withString:@""];
                c = [c stringByReplacingOccurrencesOfString:@")" withString:@""];
    
                NSMutableDictionary *dict = [NSMutableDictionary new];
                dict[@"name"] = a;
                dict[@"value"] = b;
                dict[@"url"] = c;
                dict[@"local"] = [NSString stringWithFormat:@"%ld",matchRange.location];
                dict[@"length"] = [NSString stringWithFormat:@"%ld",matchRange.length];
    
                //判断是否包含 邮箱、电话、yikao、http
                if ([NSString isValidEmail:c]||[c hasPrefix:@"yikao://"]||[NSString isValidPhone:c]||[c hasPrefix:@"http"]) {
                    [array addObject:dict];
                }
            }
            if (array.count > 0) {
                return [array.firstObject objectForKey:@"url"];
            }else{
                return  content;
            }
        }
        return content;
    }
    
    -(NSString *)handText:(NSString *)content
    {
        NSString *str = content;
        if (str) {
            //处理文本内容
            NSError *error;
            NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:@"(\\[(.*?)\\])(\\(.*?\\))" options:0 error:&error];
            NSArray *matches = [regularExpression matchesInString:str options:0 range:NSMakeRange(0, str.length)];
    
            NSMutableArray *array = [NSMutableArray array];
            for (NSTextCheckingResult *match in matches) {
                NSRange matchRange = [match range];
                
                NSString *a = [str substringWithRange:matchRange];
                //取出名称
                NSString *b = [self matchString:a toRegexString:@"(\\[(.*?)\\])"].firstObject;
                 b = [b stringByReplacingOccurrencesOfString:@"[" withString:@""];
                 b = [b stringByReplacingOccurrencesOfString:@"]" withString:@""];
    //             b = [NSString  stringWithFormat:@"%@ \n",b];
                //取出链接
                NSString *c = [self matchString:a toRegexString:@"(\\((.*?)\\))"].firstObject;
                c = [c stringByReplacingOccurrencesOfString:@"(" withString:@""];
                c = [c stringByReplacingOccurrencesOfString:@")" withString:@""];
    
                NSMutableDictionary *dict = [NSMutableDictionary new];
                dict[@"name"] = a;
                dict[@"value"] = b;
                dict[@"url"] = c;
                dict[@"local"] = [NSString stringWithFormat:@"%ld",matchRange.location];
                dict[@"length"] = [NSString stringWithFormat:@"%ld",matchRange.length];
    
                //判断是否包含 邮箱、电话、yikao、http
                if ([NSString isValidEmail:c]||[c hasPrefix:@"yikao://"]||[NSString isValidPhone:c]||[c hasPrefix:@"http"]) {
                    [array addObject:dict];
                }
            }
    
            for (NSDictionary *dict in array) {
                NSString *a = dict[@"name"];
                NSString *b = dict[@"value"];
                str = [str stringByReplacingOccurrencesOfString:a withString:b];
            }
        }
        return  str;
    }
    
    - (NSArray *)matchString:(NSString *)string toRegexString:(NSString *)regexStr
    {
    
        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:nil];
    
        NSArray * matches = [regex matchesInString:string options:0 range:NSMakeRange(0, [string length])];
    
        //match: 所有匹配到的字符,根据() 包含级
    
        NSMutableArray *array = [NSMutableArray array];
    
        for (NSTextCheckingResult *match in matches) {
    
                //以正则中的(),划分成不同的匹配部分
             NSString *component = [string substringWithRange:[match rangeAtIndex:1]];
    
             [array addObject:component];
        }
    
        return array;
    }
    @end
    
    
    • 总结:具体实现上面全有,水平有限,请指教。

    相关文章

      网友评论

          本文标题:iOS 融云消息自定义[a](b)变成a高亮 类似于MD语法

          本文链接:https://www.haomeiwen.com/subject/robemrtx.html