自制土方法实现CoreText超文本点击

来源:互联网 发布:淘宝买东西用什么支付 编辑:程序博客网 时间:2024/04/28 20:50

学习了一段时间CoreText 还是迷迷糊糊,下文是实现超链接自动识别,电话号码识别的一个土方法,大神直接飘过就好了。。。


#import <UIKit/UIKit.h>#import <CoreText/CoreText.h>typedef enum{    CLICK_TYPE_NONE = 0,    CLICK_TYPE_LINK ,    CLICK_TYPE_PHONE,}CLICK_TYPE;@interface LinkView : UIView{    CTFrameRef _frame;    NSInteger clickNum;    BOOL isClick;    CLICK_TYPE clickType;}@property (assign, nonatomic) CLICK_TYPE clickType;@property (assign, nonatomic) BOOL isClick;@property (assign, nonatomic) NSInteger clickNum;//点击的位置@property (retain, nonatomic) NSString *text;@property (retain, nonatomic) NSArray *linkArray;//存链接的Range数组@property (retain, nonatomic) NSArray *phoneArray;//存电话号码的Range数组@end 
@synthesize text;@synthesize linkArray;@synthesize phoneArray;@synthesize isClick;@synthesize clickNum;@synthesize clickType;- (void)dealloc{    [text release];    [linkArray release];    [phoneArray release];    [super dealloc];}- (id)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        // Initialization code        self.backgroundColor = [UIColor clearColor];    }    return self;}- (void)longPress:(UILongPressGestureRecognizer *)longPress{    if (longPress.state == UIGestureRecognizerStateBegan) {        [self showMenu];    }}//是否包含汉字- (BOOL) containsChinese:(NSString *)str {    for(int i = 0; i < [str length]; i++) {        int a = [str characterAtIndex:i];        if( a > 0x4e00 && a < 0x9fff)            return TRUE;    }    return FALSE;}//是否包含特殊符号,可以在这里判断超链接 结尾- (BOOL) containsSpecialSymbol:(NSString *)str{    NSSet *symbols = [NSSet setWithObjects:@" ",@",", nil];    for(int i = 0; i < [str length]; i++) {        NSString *c = [str substringWithRange:NSMakeRange(i, 1)];        if ([symbols containsObject:c]) {            return YES;        }    }    return NO;}//电话号码是否合法- (BOOL)isValidatePhone:(NSString *)phone{        return NO;//本版本不进行电话号码得判断        NSString *phoneRegex = @"\\b(1)[358][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\\b";    NSPredicate *phoneTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@" , phoneRegex];    BOOL ret = [phoneTest evaluateWithObject:phone];    return ret;}- (BOOL)isMobileNumber:(NSString *)mobileNum{    /**     * 手机号码     * 移动:134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188     * 联通:130,131,132,152,155,156,185,186     * 电信:133,1349,153,180,189     */    NSString * MOBILE = @"^1(3[0-9]|5[0-35-9]|8[025-9])\\d{8}$";    /**     10         * 中国移动:China Mobile     11         * 134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188     12         */    NSString * CM = @"^1(34[0-8]|(3[5-9]|5[017-9]|8[278])\\d)\\d{7}$";    /**     15         * 中国联通:China Unicom     16         * 130,131,132,152,155,156,185,186     17         */    NSString * CU = @"^1(3[0-2]|5[256]|8[56])\\d{8}$";    /**     20         * 中国电信:China Telecom     21         * 133,1349,153,180,189     22         */    NSString * CT = @"^1((33|53|8[09])[0-9]|349)\\d{7}$";    /**     25         * 大陆地区固话及小灵通     26         * 区号:010,020,021,022,023,024,025,027,028,029     27         * 号码:七位或八位     28         */    NSString * PHS = @"^0(10|2[0-5789]|\\d{3})\\d{7,8}$";        NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];    NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];    NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];    NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];    NSPredicate *regextestphx = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", PHS];        if (([regextestmobile evaluateWithObject:mobileNum] == YES)        || ([regextestcm evaluateWithObject:mobileNum] == YES)        || ([regextestct evaluateWithObject:mobileNum] == YES)        || ([regextestcu evaluateWithObject:mobileNum] == YES)        || ([regextestphx evaluateWithObject:mobileNum]))    {        return YES;    }    else    {        return NO;    }}//是否是连接开头- (BOOL)isValidateLink:(NSString *)Link{    if ([Link hasPrefix:@"http://"] || [Link hasPrefix:@"www."]) {        return YES;    }    return NO;}//获取链接的NSRangeArray  array 里面存的是链接的 NSRange- (NSArray *)getLinkRangeWithString:(NSString *)string{    NSMutableArray *rangeArray = [NSMutableArray arrayWithCapacity:0];    for (int loc = 0; loc < [string length]; ) {        for (int len = 0; len <= [string length] - loc; ) {            NSString *lenStr = [string substringWithRange:NSMakeRange(loc, len)];            if ([self isValidateLink:lenStr]) {                if (![self containsSpecialSymbol:lenStr]) {                    //不包含特殊字符 以特殊字符或汉字结尾,或者结尾为空                    if (loc + len == [string length]) {                        NSString *rangeStr = NSStringFromRange(NSMakeRange(loc, len));                        [rangeArray addObject:rangeStr];                        loc += len - 1;                        break;                    }else{                        if ( [self containsSpecialSymbol:[string substringWithRange:NSMakeRange(loc + len, 1)]]) {                            NSString *rangeStr = NSStringFromRange(NSMakeRange(loc, len));                            [rangeArray addObject:rangeStr];                            loc += len - 1;                            break;                        }                    }                }            }            len++;        }        loc++;    }    return rangeArray;}//获取电话的NSRange- (NSArray *)getPhoneRangeWithString:(NSString *)string{        NSMutableArray *rangeArray = [[NSMutableArray alloc]initWithCapacity:0];    for (int loc = 0; loc < [string length]; ) {        for (int len = 0; len <= [string length] - loc; ) {            NSString *lenStr = [string substringWithRange:NSMakeRange(loc, len)];//            NSLog(@"lenStr: %@",lenStr);            if ([self isMobileNumber:lenStr]) {                NSString *rangeStr = NSStringFromRange(NSMakeRange(loc, len));                [rangeArray addObject:rangeStr];                loc += len - 1;                break;            }            len++;        }        loc++;    }    return rangeArray;}//设置换行模式- (void)setAttString:(NSMutableAttributedString *)attString LineBreakMode:(CTLineBreakMode)mode{    CTParagraphStyleSetting lineBreakMode;    CTLineBreakMode lineBreak = mode;    lineBreakMode.spec = kCTParagraphStyleSpecifierLineBreakMode;    lineBreakMode.value = &lineBreak;    lineBreakMode.valueSize = sizeof(CTLineBreakMode);        CTParagraphStyleSetting settings[] = {        lineBreakMode    };        CTParagraphStyleRef style = CTParagraphStyleCreate(settings, 1);        NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(id)style forKey:(id)kCTParagraphStyleAttributeName];        [attString addAttributes:attributes range:NSMakeRange(0, [attString length])];}- (void)drawRect:(CGRect)rect{        CGContextRef context = UIGraphicsGetCurrentContext();        CGContextSetTextMatrix(context, CGAffineTransformIdentity);        CGContextSaveGState(context);        //x,y轴方向移动    CGContextTranslateCTM(context, 0, self.bounds.size.height);    //缩放x,y轴方向缩放,-1.0为反向1.0倍,坐标系转换,沿x轴翻转180度    CGContextScaleCTM(context, 1.0, -1.0);    CGMutablePathRef path = CGPathCreateMutable();        CGPathAddRect(path, NULL, self.bounds);        NSMutableAttributedString *attString = [[NSMutableAttributedString alloc]initWithString:self.text];        [attString beginEditing];        //设置字体属性    CTFontRef font = [self CTFontRefCTFontCreateFromUIFont:[UIFont systemFontOfSize:16.0f]];    [attString addAttribute:(id)kCTFontAttributeName value:(id)font range:NSMakeRange(0, [attString length])];        //让文字细一点////    CGFloat widthValue = -1.0;        CFNumberRef strokeWidth = CFNumberCreate(NULL,kCFNumberFloatType,&widthValue);        [attString addAttribute:(NSString*)(kCTStrokeWidthAttributeName) value:(id)strokeWidth range:NSMakeRange(0,[text length])];        [attString addAttribute:(NSString*)(kCTStrokeColorAttributeName) value:(id)[[UIColor whiteColor]CGColor] range:NSMakeRange(0,[text length])];    /////细一点            self.phoneArray = [self getPhoneRangeWithString:self.text];    self.linkArray = [self getLinkRangeWithString:self.text];        //设置字体颜色    [attString addAttribute:(id)kCTForegroundColorAttributeName value:(id)[UIColor blackColor].CGColor range:NSMakeRange(0, [attString length])];    for (int i = 0; i < [self.linkArray count]; i ++) {        [attString addAttribute:(id)kCTForegroundColorAttributeName value:(id)[UIColor blueColor].CGColor range:NSRangeFromString([self.linkArray objectAtIndex:i])];        [attString addAttribute:(id)kCTUnderlineStyleAttributeName value:(id)[NSNumber numberWithInt: kCTUnderlineStyleSingle] range:NSRangeFromString([self.linkArray objectAtIndex:i])];        [attString addAttribute:(id)kCTUnderlineColorAttributeName value:(id)[UIColor greenColor] range:NSRangeFromString([self.linkArray objectAtIndex:i])];    }    for (int i = 0; i < [self.phoneArray count]; i ++) {        [attString addAttribute:(id)kCTForegroundColorAttributeName value:(id)[UIColor redColor].CGColor range:NSRangeFromString([self.phoneArray objectAtIndex:i])];    }    if (self.isClick) {        switch (self.clickType) {            case CLICK_TYPE_LINK:                [attString removeAttribute:(id)kCTForegroundColorAttributeName range:NSRangeFromString([self.linkArray objectAtIndex:self.clickNum])];                [attString addAttribute:(id)kCTForegroundColorAttributeName value:(id)[UtilityHelper colorWithHexString:@"#002f6f"].CGColor range:NSRangeFromString([self.linkArray objectAtIndex:self.clickNum])];                break;            case CLICK_TYPE_PHONE:                [attString removeAttribute:(id)kCTForegroundColorAttributeName range:NSRangeFromString([self.phoneArray objectAtIndex:self.clickNum])];                [attString addAttribute:(id)kCTForegroundColorAttributeName value:(id)[UIColor redColor].CGColor range:NSRangeFromString([self.phoneArray objectAtIndex:self.clickNum])];                break;            default:                break;        }            }    //换行模式//    [self setAttString:attString LineBreakMode:kCTLineBreakByWordWrapping];    [self setText:attString Alignment:kCTTextAlignmentLeft lineBreakMode:kCTLineBreakByCharWrapping];        [attString endEditing];        CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString);        CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, [attString length]), path, NULL);    _frame = frame;    CTFrameDraw(frame, context);        //    CFRelease(frame);        CFRelease(frameSetter);        CFRelease(path);}//设置换行和对其方式-(void)setText:(NSMutableAttributedString *)attString Alignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode {[self setText:attString Alignment:alignment lineBreakMode:lineBreakMode range:NSMakeRange(0,[attString length])];}-(void)setText:(NSMutableAttributedString *)attString Alignment:(CTTextAlignment)alignment lineBreakMode:(CTLineBreakMode)lineBreakMode range:(NSRange)range {// kCTParagraphStyleAttributeName > kCTParagraphStyleSpecifierAlignmentCTParagraphStyleSetting paraStyles[2] = {{.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void*)&alignment},{.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void*)&lineBreakMode},};CTParagraphStyleRef aStyle = CTParagraphStyleCreate(paraStyles, 2);[attString removeAttribute:(NSString*)kCTParagraphStyleAttributeName range:range]; // Work around for Apple leak[attString addAttribute:(NSString*)kCTParagraphStyleAttributeName value:(id)aStyle range:range];CFRelease(aStyle);}//根据 点 判断 字符indexCFIndex CFIndexGet(CGPoint point,CTFrameRef frame){    //获取每一行    CFArrayRef lines = CTFrameGetLines(frame);    CGPoint origins[CFArrayGetCount(lines)];    //获取每行的原点坐标    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);    CTLineRef line = NULL;    CGPoint lineOrigin = CGPointZero;    for (int i= 0; i < CFArrayGetCount(lines); i++)    {        CGPoint origin = origins[i];        CGPathRef path = CTFrameGetPath(frame);        //获取整个CTFrame的大小        CGRect rect = CGPathGetBoundingBox(path);                //坐标转换,把每行的原点坐标转换为uiview的坐标体系        CGFloat y = rect.origin.y + rect.size.height - origin.y;        //判断点击的位置处于那一行范围内        if ((point.y <= y) && (point.x >= origin.x))        {            line = CFArrayGetValueAtIndex(lines, i);            lineOrigin = origin;            break;        }    }    point.x -= lineOrigin.x;    //获取点击位置所处的字符位置,就是相当于点击了第几个字符    CFIndex index = CTLineGetStringIndexForPosition(line, point);        return index;}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    UITouch *touch = [touches anyObject];    //获取触摸点击当前view的坐标位置    CGPoint location = [touch locationInView:self];        CFIndex index = CFIndexGet(location, _frame);        for (int i = 0; i< [self.linkArray count]; i++) {        NSString *rangeStr = [self.linkArray objectAtIndex:i];        NSRange range = NSRangeFromString(rangeStr);        if (index >= range.location && index <= range.location + range.length) {            self.isClick = YES;            self.clickNum = i;            self.clickType = CLICK_TYPE_LINK;            [self setNeedsDisplay];        }    }    for (int i = 0; i< [self.phoneArray count]; i++) {        NSString *rangeStr = [self.phoneArray objectAtIndex:i];        NSRange range = NSRangeFromString(rangeStr);        if (index >= range.location && index <= range.location + range.length) {            self.isClick = YES;            self.clickNum = i;            self.clickType = CLICK_TYPE_PHONE;            [self setNeedsDisplay];        }    }    }- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{    UITouch *touch = [touches anyObject];    //获取触摸点击当前view的坐标位置    CGPoint location = [touch locationInView:self];        CFIndex index = CFIndexGet(location, _frame);    //    NSLog(@"indexEnd:%ld",index);    for (int i = 0; i< [self.linkArray count]; i++) {        NSString *rangeStr = [self.linkArray objectAtIndex:i];        NSRange range = NSRangeFromString(rangeStr);        if (index >= range.location && index <= range.location + range.length) {            if (i == self.clickNum) {                NSMutableString *linkText = [NSMutableString stringWithString:[self.text substringWithRange:range]];                if ([linkText hasPrefix:@"www:"]) {                    linkText = [NSMutableString stringWithFormat:@"http://%@",linkText];                }                [UtilityHelper gatrackEvent:@"客户聊天详情页" label:@"跳转超链接"];                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:linkText]];            }        }    }    for (int i = 0; i< [self.phoneArray count]; i++) {        NSString *rangeStr = [self.phoneArray objectAtIndex:i];        NSRange range = NSRangeFromString(rangeStr);        if (index >= range.location && index <= range.location + range.length) {            if (i == self.clickNum) {                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel://%@",[self.text substringWithRange:range]]]];            }        }    }    self.isClick = NO;    self.clickNum = -1;    self.clickType = CLICK_TYPE_NONE;    [self setNeedsDisplay];}- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{    self.isClick = NO;    self.clickNum = -1;    self.clickType = CLICK_TYPE_NONE;    [self setNeedsDisplay];}//根据UIFont 获取 CTFontRef- (CTFontRef)CTFontRefCTFontCreateFromUIFont:(UIFont *)font{    CTFontRef ctFont =CTFontCreateWithName((CFStringRef)font.fontName,                                           font.pointSize,                                           NULL);    return ctFont;}@end



原创粉丝点击