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

效果

分析

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

源码

https://github.com/YouXianMing/TTTAttributeLabelView

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
时间: 2024-10-08 08:30:29

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

富文本及点击方法

类似这种效果及功能. static NSString *const kStringTip = @"如有问题,请拨打客服电话:010-57512156"; _tip = [[UITextView alloc] init]; _tip.width = SCREEN_WIDTH - kSideOffset * 2.0; _tip.text = kStringTip; [_tip sizeToFit]; _tip.top = labelY + 29; _tip.left = kSideOffs

My97DatePicker日历的平面显示,不是文本框点击事件后显示

二. 功能及示例 2. 特色功能 平面显示 日期控件支持平面显示功能,只要设置一下eCont属性就可以把它当作日历来使用了,无需触发条件,直接显示在页面上 示例2-1 平面显示演示 <div id="div1"></div> <script> WdatePicker({eCont:'div1',onpicked:function(dp){alert('你选择的日期是:'+dp.cal.getDateStr())}}) </script>

Android viewpager 嵌套 viewpager滑动 点击事件冲突解决方案

为了解决这个问题.可以自定义viewpager,然后在里面监听首饰,自定义点击事件 package com.hpuvoice.view; import android.content.Context; import android.graphics.PointF; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.GestureDetector; impo

AndroidRichText 让Textview轻松的支持富文本(图像ImageSpan、点击效果等等类似QQ微信聊天)

代码地址:https://github.com/Luction/AndroidRichText AndroidRichText帮助实现像QQ,微信一样的,一个TextView里既有文字又有表情又有图片的效果,采用插件化的框架,代码简单,可拓展性强. 基础框架包只有四个java文件, RichTextWrapper :TextView的包裹类,实现支持富文本,通过new RichTextWrapper(TextView v)来构造. RTMovementMethod: 继承自Android原生的L

CoreText实现图文混排之点击事件

今天呢,我们继续把CoreText图文混排的点击事件补充上,这样我们的图文混排也算是圆满了. 哦,上一篇的链接在这里 http://www.jianshu.com/p/6db3289fb05d CoreText实现图文混排.所有需要用到的准备知识都在上一篇,没有赶上车的朋友可以去补个票~ 上正文. CoreText做图文混排之点击事件 主要思路 我们知道,CoreText是基于UIView去绘制的,那么既然有UIView,就有 -(void)touchesBegan:(NSSet)touches

CoreText实现图文混排之点击事件-b

CoreText实现图文混排之点击事件 主要思路 我们知道,CoreText是基于UIView去绘制的,那么既然有UIView,就有 -(void)touchesBegan:(NSSet<UITouch *> )touches withEvent:(UIEvent )event方法,我们呢,就是基于这个方法去做点击事件的. 通过touchBegan方法拿到当前点击到的点,然后通过坐标判断这个点是否在某段文字上,如果在则触发对应事件. 上面呢就是主要思路.接下来呢,我们来详细讲解一下.还是老规矩

类百度富文本编辑器文件上传。

在项目安开发中,有一个需求是用户需上传特定的text文件,根据此文件判断设备的状态. 以上是需求,此时需要在页面上用到文件上传的功能,原始的文件上传样式不好看,自己此段时间一直都在用layui前端框架写页面,本想用layui官网上给的文件上传功能的,但是经理说上传的文件不能只显示文件名, 还要配上一张图片.layui上传文件的模式都是: 显然这个版本的文件上传还没有符合项目的需要. 在接下来的对比中,看到了百度富文本编辑器的文件上传和图片上传样式:       因为这个功能是在百度富文本编辑器工

textView富文本点击事件

NSDictionary * attDic = [NSDictionary dictionaryWithObjectsAndKeys:RGBCOLOR(31, 132, 204),NSForegroundColorAttributeName,[NSNumber numberWithInteger:NSUnderlineStyleSingle],NSUnderlineStyleAttributeName,@“url”,NSLinkAttributeName, nil]; 如果想要跳转viewCon

实现一个简易的富文本编辑器(二):给富文本添加自定义事件

为富文本添加一个提交按钮,点击按钮可以获取富文本内容.但是在提交前或者提交后我想做一些操作,比如内容校验内容清空等等. 我们直接在该按钮上绑定点击事件同样可以达到目的,但是为了组件化,所以本例打算为提交按钮自定义beforeSubmit.afterSubmit两个事件. 1.创建发布订阅者对象 前文说到,事件系统是发布-订阅模式的一个实现,模式给事件发布函数与事件处理函数进行解耦,使得两者无直接调用关系. 简易发布订阅者对象实现如下: var Event = { // _cachePool :