富文本带点击事件的解决方案

简介:

富文本带点击事件的解决方案

 

效果

 

分析

富文本中要添加点击link事件,需要深入到CoreText里面才能够解决,本人将TTTAttributedLabel进行了封装(封装并不完全,以后会继续完善),简化了操作.

 

源码

https://github.com/YouXianMing/UI-Component-Collection

https://github.com/TTTAttributedLabel/TTTAttributedLabel


//
//  TTTAttributeLabelView.h
//  TappedLabel
//
//  Created by YouXianMing on 15/6/13.
//  Copyright (c) 2015年 YouXianMing. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreText/CoreText.h>
@class TTTAttributeLabelView;

@protocol TTTAttributeLabelViewDelegate <NSObject>
@optional
/**
 *  获取超链接点击事件
 *
 *  @param attributeLabelView 实例对象
 *  @param flag               设置的被点击的flag
 */
- (void)TTTAttributeLabelView:(TTTAttributeLabelView *)attributeLabelView linkFlag:(NSString *)flag;

@end

@interface TTTAttributeLabelView : UIView

/**
 *  代理
 */
@property (nonatomic, weak)   id <TTTAttributeLabelViewDelegate> delegate;

/**
 *  输入的富文本
 */
@property (nonatomic, strong) NSAttributedString   *attributedString;

/**
 *  通常状态链接颜色 + 通常状态链接下划线样式 + 点击链接时链接颜色 + 点击链接时下划线样式
 */
@property (nonatomic, strong) UIColor           *linkColor;
@property (nonatomic)         CTUnderlineStyle   linkUnderLineStyle;        // 默认值为kCTUnderlineStyleNone
@property (nonatomic, strong) UIColor           *activeLinkColor;
@property (nonatomic)         CTUnderlineStyle   activelinkUnderLineStyle;  // 默认值为kCTUnderlineStyleNone

/**
 *  添加超链接文本的文本range
 *
 *  @param linkStringRange 超链接文本的文本range
 *  @param flag            该文本的标记
 */
- (void)addLinkStringRange:(NSRange)linkStringRange flag:(NSString *)flag;

/**
 *  重置
 */
- (void)reset;

/**
 *  渲染文本
 */
- (void)render;

/**
 *  重新计算尺寸
 */
- (void)resetSize;

/**
 *  计算执行resetSize后的size
 *
 *  @param attributedString 富文本
 *  @param width            给定一个宽度
 *
 *  @return 计算好的size
 */
+ (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString withFixedWidth:(CGFloat)width;

@end


//
//  TTTAttributeLabelView.m
//  TappedLabel
//
//  Created by YouXianMing on 15/6/13.
//  Copyright (c) 2015年 YouXianMing. All rights reserved.
//

#import "TTTAttributeLabelView.h"
#import "TTTAttributedLabel.h"

/**
 *  将CFString转换为NSString
 *
 *  @param cfString
 *
 *  @return 转换后的CFString
 */
static inline NSString*  nsStringWithCfString(CFStringRef cfString) {
    return (__bridge NSString *)cfString;
}

/*--------------------------------------------------------------------------------*/

@interface RangeFlag : NSObject

@property (nonatomic, strong) NSString *flag;
@property (nonatomic)         NSRange   range;

+ (RangeFlag *)rangeFlagWithFlag:(NSString *)flag range:(NSRange)range;

@end

@implementation RangeFlag

+ (RangeFlag *)rangeFlagWithFlag:(NSString *)flag range:(NSRange)range {
    RangeFlag *rangeFlag = [RangeFlag new];
    rangeFlag.flag       = flag;
    rangeFlag.range      = range;
    
    return rangeFlag;
}

@end


/*--------------------------------------------------------------------------------*/


@interface TTTAttributeLabelView () <TTTAttributedLabelDelegate>

@property (nonatomic, strong) TTTAttributedLabel   *label;
@property (nonatomic, strong) NSMutableArray       *links;

@end

@implementation TTTAttributeLabelView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        
        [self setup];
    }
    
    return self;
}

- (void)setup {
    
    // 存储超链接的数组
    self.links                      = [NSMutableArray array];

    // label
    self.label                      = [[TTTAttributedLabel alloc] initWithFrame:self.bounds];
    self.label.delegate             = self;
    self.label.extendsLinkTouchArea = NO;
    self.label.verticalAlignment    = TTTAttributedLabelVerticalAlignmentTop;
    self.label.numberOfLines        = 0;
    [self addSubview:self.label];
}

- (void)reset {
    self.label.text = nil;
    [self.links removeAllObjects];
}

- (void)render {
    if (self.attributedString.string.length <= 0) {
        return;
    }
    
    self.label.text = self.attributedString;
    
    [self linkStyles];
    
    [self addLinks];
}

- (void)addLinks {

    for (int count = 0; count < self.links.count; count++) {
        
        RangeFlag *rangeflag = self.links[count];
        [self.label addLinkToURL:[NSURL URLWithString:rangeflag.flag] withRange:rangeflag.range];
    }
}

- (void)linkStyles {
    
    
    UIColor           *linkColor                 = (self.linkColor == nil ? [UIColor blueColor] : self.linkColor);
    CTUnderlineStyle   linkUnderLineStyle        = [self checkEnumValueValid:self.linkUnderLineStyle];
    UIColor           *activeLinkColor           = (self.activeLinkColor == nil ? [UIColor redColor] : self.activeLinkColor);
    CTUnderlineStyle   activelinkUnderLineStyle  = [self checkEnumValueValid:self.activelinkUnderLineStyle];

    
    // 没有点击时候的样式
    self.label.linkAttributes       = @{nsStringWithCfString(kCTForegroundColorAttributeName) : linkColor,
                                        nsStringWithCfString(kCTUnderlineStyleAttributeName)  : [NSNumber numberWithInt:linkUnderLineStyle]};
    
    // 点击时候的样式
    self.label.activeLinkAttributes = @{nsStringWithCfString(kCTForegroundColorAttributeName) : activeLinkColor,
                                        nsStringWithCfString(kCTUnderlineStyleAttributeName)  : [NSNumber numberWithInt:activelinkUnderLineStyle]};
}

- (CTUnderlineStyle)checkEnumValueValid:(CTUnderlineStyle)style {
    if (style == kCTUnderlineStyleNone || style == kCTUnderlineStyleSingle || style == kCTUnderlineStyleThick || style == kCTUnderlineStyleDouble) {
        return style;
    } else {
        return kCTUnderlineStyleSingle;
    }
}

- (void)addLinkStringRange:(NSRange)linkStringRange flag:(NSString *)flag {
    [self.links addObject:[RangeFlag rangeFlagWithFlag:flag range:linkStringRange]];
}

- (void)resetSize {
    [self.label sizeToFit];
    
    CGFloat x      = self.frame.origin.x;
    CGFloat y      = self.frame.origin.y;
    CGFloat width  = self.label.frame.size.width;
    CGFloat height = self.label.frame.size.height;

    self.frame = CGRectMake(x, y, width, height);
}

+ (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString withFixedWidth:(CGFloat)width {
    return [TTTAttributedLabel sizeThatFitsAttributedString:attributedString
                                            withConstraints:CGSizeMake(width, 0)
                                     limitedToNumberOfLines:0];
}

#pragma mark - 超链接代理
- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url {
    if (_delegate && [_delegate respondsToSelector:@selector(TTTAttributeLabelView:linkFlag:)]) {
        [_delegate TTTAttributeLabelView:self linkFlag:url.absoluteString];
    }
}

@end

使用


//
//  ViewController.m
//  TapLabelView
//
//  Created by YouXianMing on 15/6/13.
//  Copyright (c) 2015年 YouXianMing. All rights reserved.
//

#import "ViewController.h"
#import "TTTAttributeLabelView.h"
#import "NSString+RichText.h"
#import "TTTAttributedLabel.h"

@interface ViewController () <TTTAttributeLabelViewDelegate>

@property (nonatomic, strong) TTTAttributeLabelView  *attributeLabelView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor blackColor];
    
    // 创建富文本
    NSString *string = @"Between the husband and earth, each master. All Gou Fei Wu, although a little and Mo to take. YouXianMing but the river breeze, and the mountain of the moon, ear and sound, eyes meet and fineness. Take no ban, be inexhaustible. Is also the creator of the endless Tibet, and I and the children were appropriate.\n夫天地之间,物各有主。苟非吾之所有,虽一毫而莫取。惟江上之清风,与山间之明月,耳得之而为声,目遇之而成色。 取之无禁,用之不竭。是造物者之无尽藏也,而吾与子之所共适。";
    NSMutableParagraphStyle *style = [NSMutableParagraphStyle new];
    style.lineSpacing              = 4.f;
    style.paragraphSpacing         = style.lineSpacing * 4;
    style.alignment                = NSTextAlignmentCenter;
    NSAttributedString *attributedString  = \
        [string createAttributedStringAndConfig:@[[ConfigAttributedString foregroundColor:[UIColor whiteColor] range:string.range],
                                                  [ConfigAttributedString paragraphStyle:style range:string.range],
                                                  [ConfigAttributedString font:[UIFont fontWithName:@"AppleSDGothicNeo-UltraLight" size:14.f] range:string.range]]];
    
    // 初始化对象
    self.attributeLabelView                    = [[TTTAttributeLabelView alloc] initWithFrame:CGRectMake(10, 50, 300, 0)];
    self.attributeLabelView.attributedString   = attributedString;
    self.attributeLabelView.delegate           = self;
    self.attributeLabelView.linkColor          = [UIColor cyanColor];
    
    // 添加超链接
    NSRange range1 = [string rangeOfString:@"YouXianMing"];
    [self.attributeLabelView addLinkStringRange:range1 flag:@"link1"];
    
    NSRange range2 = [string rangeOfString:@"inexhaustible"];
    [self.attributeLabelView addLinkStringRange:range2 flag:@"link2"];
    
    NSRange range3 = [string rangeOfString:@"耳得之而为声,目遇之而成色。"];
    [self.attributeLabelView addLinkStringRange:range3 flag:@"link3"];
    
    // 进行渲染
    [self.attributeLabelView render];
    [self.attributeLabelView resetSize];
    [self.view addSubview:self.attributeLabelView];
}

- (void)TTTAttributeLabelView:(TTTAttributeLabelView *)attributeLabelView linkFlag:(NSString *)flag {
    NSLog(@"%@", flag);
}

@end


目录
相关文章
|
3月前
Uniapp 各类 button按钮
Uniapp 各类 button按钮
22 0
|
15天前
简单讲述ondragstart、drag、ondragend、ondragenter、ondragover、ondrop、ondragleave七个与拖拽相关的监听事件,并运用实现拖拽过程放置样式变化
简单讲述ondragstart、drag、ondragend、ondragenter、ondragover、ondrop、ondragleave七个与拖拽相关的监听事件,并运用实现拖拽过程放置样式变化
|
8月前
【分享】宜搭抽屉内嵌入表单,表单提交后自动隐藏抽屉
抽屉内嵌入表单,表单提交后自动隐藏抽屉
457 1
|
8月前
|
JavaScript
原生js实现拖拽功能
原生js实现拖拽功能
48 0
|
前端开发 程序员
【前端】使用样式选择器before和after布局,以及简单的表单输入框布局
本篇文章来讲解下样式选择器before和after的用法,以及doctype的作用 有时候真的需要温故而知新,用了那么多年的前端,对一些知识点灵活运用加点小技巧也是挺有趣的
134 0
|
前端开发 JavaScript
|
前端开发 JavaScript
【JavaScript】点击那个按钮,那个按钮变色,其他方案
【JavaScript】点击那个按钮,那个按钮变色,其他方案
128 0
【JavaScript】点击那个按钮,那个按钮变色,其他方案
|
存储 SQL 测试技术
【实现】表单控件的UI布局,实现方式
 一、先说一下表单控件要实现的功能吧。        1、绘制UI,包括表格(Table)的绘制,也就是TR 、TD,TR是多少行,TD是有多少列;包括子控件的控件,TextBox、DropDownList、CheckBoxList等控件的加载、描述(宽度、最大字符数、填充item)等。
1458 0
|
Android开发 iOS开发 索引
Xamarin自定义布局系列——支持无限滚动的自动轮播视图CarouselView
原文:Xamarin自定义布局系列——支持无限滚动的自动轮播视图CarouselView 背景简述 自动轮播视图(CarouselView)在现在App中的地位不言而喻,绝大多数的App中都有类似的视图,无论是WebApp还是Native App。
1831 0