原创博文,未经作者允许,不允许转载

DTCoreText自带的DTAttributedTextCell在显示html的时候  会占用整个cell的大小,当我们需要的形式比较灵活的时候,或者想在cell上自定义添加更多的东西的时候  DTAttributedTextCell 就会变的不够用 需要我门根据DTAttributedTextCell的原理,自己写一个cell

例如 我们希望cell左边是一个图片,然后右边剩下的区域是一个DTAttributedTextContentView用来显示html 这个图片在点击cell的时候会改变

步骤


1.首先 仍然是将DTCoreText添加到我们自己的工程文件中

2.创建UITableViewCell的子类 MyCell.h MyCell.m

MyCell.h

 1 @interface MyCell : UITableViewCell
 2 {
 3
 4      IBOutlet UIImageView *imageView;
 5      IBOutlet DTAttributedTextContentView *_attributedTextContextView;
 6 }
 7
 8 @property(nonatomic,retain)UIImageView *imageView;
 9
10 @property (nonatomic, strong) NSAttributedString *attributedString;
11 @property (nonatomic, readonly) DTAttributedTextContentView *attributedTextContextView;
12 - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier accessoryType:(UITableViewCellAccessoryType)accessoryType;
13
14 - (void)setHTMLString:(NSString *)html;
15
16 - (CGFloat)requiredRowHeightInTableView:(UITableView *)tableView;
17
18 @end

storyboard中 左边是图片  右边是UIView  将其class设为DTAttributedTextContentView

将cell的class设置为MyCell

并且 都与MyCell进行连接

这样当我们调用mycell的 setHTMLString:(NSString*)html方法时候  实际上是在设置右边的attributedTextContextView.attributedString  为我们解析过后的string  然后显示出来

MyCell.m


直接将DTAttributedTextCell.m中的所有代码代码复制过来即可 但是要更改几处地方

 1 #import "MyCell.h"
 2 #import "DTCoreText.h"
 3 #import "DTAttributedTextCell.h"
 4 @implementation MyCell
 5 {
 6
 7     NSAttributedString *_attributedString;
 8     //DTAttributedTextContentView *_attributedTextContextView;   //改动1
 9
10     NSUInteger _htmlHash; // preserved hash to avoid relayouting for same HTML
11 }
12 @synthesize attributedString = _attributedString;
13 @synthesize attributedTextContextView = _attributedTextContextView;
14
15 @synthesize imageView;   //添加这一句  改动2

在.m文件中 {}中的属性是私有属性  由于我们在MyCell.h中声明了 DTAttributedTextContentView *_attributedTextContextView;   所以这里不用再次声明

 1 - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier accessoryType:(UITableViewCellAccessoryType)accessoryType
 2 {
 3     self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
 4     if (self)
 5     {
 6         // don't know size jetzt because there's no string in it
 7
 8         //_attributedTextContextView = [[DTAttributedTextContentView alloc] init];
 9         //_attributedTextContextView.frame=CGRectMake(100, 100, 40, 50);
10
11         _attributedTextContextView.edgeInsets = UIEdgeInsetsMake(5, 5, 5, 5);
12
13     //    [self.contentView addSubview:_attributedTextContextView];
14
15
16
17
18     }
19     return self;
20 }

这里面注释掉的代码是 原来的cell  由于DTAttributedTextCell 没有用storyboard或者 xib 所以 它上面的

DTAttributedTextContentView 是用代码  在初始化的时候 addSubView上去的  我们的MyCell 是用storyboard显式创建的

所以这里面不用这些初始化代码

这时需要更改的几个地方 其他的直接复制过来即可

3.在tableView中引用MyCell

这里只贴出关键部分代码

 1 - (void)configureCell:(MyCell *)cell forIndexPath:(NSIndexPath *)indexPath
 2 {
 3
 4     NSString *html=[array objectAtIndex:indexPath.row];
 5     [cell setHTMLString:html];
 6     cell.imageView.image=[UIImage imageNamed:@"XX"];
 7
 8     cell.attributedTextContextView.shouldDrawImages = YES;
 9 }
10
11 - (MyCell *)tableView:(UITableView *)tableView preparedCellForIndexPath:(NSIndexPath *)indexPath
12 {
13         //DTCoreTest Demo 中的源代码  这里将 其注释掉
14     //static NSString *cellIdentifier = @"name";
15
16     if (!cellCache)
17     {
18         cellCache = [[NSCache alloc] init];
19     }
20
21     // workaround for iOS 5 bug
22     NSString *key = [NSString stringWithFormat:@"%d-%d", indexPath.section, indexPath.row];
23
24     MyCell *cell = [cellCache objectForKey:key];
25
26     if (!cell)
27     {
28         // reuse does not work for variable height
29         //cell = (DTAttributedTextCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
30
31         if (!cell)
32         {
33                         //DTCoreTest Demo 中的源代码  这里将 其注释掉
34             //cell = [[MyCell alloc] initWithReuseIdentifier:cellIdentifier accessoryType:UITableViewCellAccessoryDisclosureIndicator];
35             //这一句是引用我们自定义的cell的重点代码
36             cell=[tableView dequeueReusableCellWithIdentifier:@"name"];
37
38
39         }
40
41         // cache it
42         [cellCache setObject:cell forKey:key];
43     }
44
45     [self configureCell:cell forIndexPath:indexPath];
46
47     return cell;
48 }
49
50 // disable this method to get static height = better performance
51 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
52 {
53     MyCell *cell = (MyCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];
54     //下面一句是非常重要的一句代码
55     return  cell.attributedTextContextView.frame.size.height+10;
56    //DTCoreTest Demo 中的源代码  这里将 其注释掉 改为上面一句
57     //return [cell requiredRowHeightInTableView:tableView];
58 }
59
60 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
61 {
62     MyCell *cell = (MyCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];
63
64     return cell;
65 }

填充数据方面的代码比较简单  这里就不贴出来了

这时后点击运行  显示如下图所示

很明显出现了错误,  DTAttributedTextContentView 仍然占据整个cell   而imageView  就是那个A  显示的位置正确 但却因为

DTAttributedTextContentView 占据整个cell  而导致其覆盖在上面  并没有按照我们设计布局的  左边显示 A  右边显示html

但是有一点是确定的     自适应高度没有问题


我们先来分析一下tableView中 那些关键代码的  运行流程

首先调用

1 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
2 {
3     MyCell *cell = (MyCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];
4
5     return  cell.attributedTextContextView.frame.size.height+10;
6
7     //return [cell requiredRowHeightInTableView:tableView];
8 }

来确定每一个cell的高度

在这里面继续调用

 1 - (MyCell *)tableView:(UITableView *)tableView preparedCellForIndexPath:(NSIndexPath *)indexPath
 2 {
 3     //static NSString *cellIdentifier = @"name";
 4
 5     if (!cellCache)
 6     {
 7         cellCache = [[NSCache alloc] init];
 8     }
 9
10     // workaround for iOS 5 bug
11     NSString *key = [NSString stringWithFormat:@"%d-%d", indexPath.section, indexPath.row];
12
13     MyCell *cell = [cellCache objectForKey:key];
14
15     if (!cell)
16     {
17         // reuse does not work for variable height
18         //cell = (DTAttributedTextCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
19
20         if (!cell)
21         {
22             //cell = [[MyCell alloc] initWithReuseIdentifier:cellIdentifier accessoryType:UITableViewCellAccessoryDisclosureIndicator];
23             cell=[tableView dequeueReusableCellWithIdentifier:@"name"];
24
25
26         }
27
28         // cache it
29         [cellCache setObject:cell forKey:key];
30     }
31
32     [self configureCell:cell forIndexPath:indexPath];
33
34     return cell;
35 }

这个方法中 有两句核心代码  其他的都是一些缓存相关 看一下代码很好理解

cell=[tableView dequeueReusableCellWithIdentifier:@"name"];

[self configureCell:cell forIndexPath:indexPath];

第一句  是在缓存中没有可取的 cell的时候 从tableView的可重用队列中取出一个cell实例(可参考《自定义UITableViewCell的理解一文》)

第二句 是配置cell  接下来看看  这个方法的代码

1 - (void)configureCell:(MyCell *)cell forIndexPath:(NSIndexPath *)indexPath
2 {
3
4     NSString *html=[array objectAtIndex:indexPath.row];
5     [cell setHTMLString:html];
6     cell.imageView.image=[UIImage imageNamed:@"xxx"];7
8     cell.attributedTextContextView.shouldDrawImages = YES;
9 }

这里面是初始化cell上的图片 以及DTAttributedTextContentView

核心代码是[cell setHTMLString:html];  前面有介绍

通过单步跟踪调试  跟踪以后的调用流程

这里先进入setHTMLString

 1 - (void)setHTMLString:(NSString *)html
 2 {
 3     // we don't preserve the html but compare it's hash
 4     NSUInteger newHash = [html hash];
 5
 6     if (newHash == _htmlHash)
 7     {
 8         return;
 9     }
10
11     _htmlHash = newHash;
12
13     NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];
14     NSAttributedString *string = [[NSAttributedString alloc] initWithHTML:data documentAttributes:NULL];
15     self.attributedString = string;
16 }

这一段的核心代码是最后一句   前面大部分都是对html的处理 包括解析等等 如果单步跟踪进入

会发现

NSAttributedString *string = [[NSAttributedString alloc] initWithHTML:data documentAttributes:NULL];调用的很

深 很深 里面包括对html的各种处理  解析 等等

在跟踪的时候发现了设置 显示的html的字体大小的方法  

在DTHTMLAttributedStringBuilder.m中 的 buildString方法中 找到textScale  变量  更改其值变能更改显示在cell上的字体的大小 

接着调用最后一句 self.attributedString = string  由于MyCell.m重写了setAttributedString 方法所以调用之

 1 - (void)setAttributedString:(NSAttributedString *)attributedString
 2 {
 3     if (_attributedString != attributedString)
 4     {
 5         _attributedString = attributedString;
 6
 7         // passthrough
 8         _attributedTextContextView.attributedString = _attributedString;
 9     }
10 }

很显然 这里面的最后一句是最重要的代码  只要设置DTAttributedTextContentView 的attributedString 为解析好的要显示的

html   便能直接显示出来

这里继续单步跟踪进入  会进入DTAttributedTextContentView 的一系列方法中  而这些方法是揭开 为什么没有按照我门设计的布局显示的线索

DTAttributedTextContentView.m

 1 - (void)setAttributedString:(NSAttributedString *)string
 2 {
 3     if (_attributedString != string)
 4     {
 5
 6         _attributedString = [string copy];
 7
 8         // new layout invalidates all positions for custom views
 9         [self removeAllCustomViews];
10
11         [self relayoutText];
12     }
13 }

我门关心的是为什么我们设计的DTAttributedTextContentView  是在左边的区域  但是 运行的时候却充满正的cell  这肯定与

最后一句代码有关   relayoutText


继续跟踪  进入了最重要的方法  也是解决问题的关键方法  relayoutText

 

 1 - (void)relayoutText
 2 {
 3     // Make sure we actually have a superview before attempting to relayout the text.
 4     if (self.superview) {
 5         // need new layouter
 6         self.layouter = nil;
 7         self.layoutFrame = nil;
 8
 9         // remove all links because they might have merged or split
10         [self removeAllCustomViewsForLinks];
11
12         if (_attributedString)
13         {
14             // triggers new layout
15
16             CGSize neededSize = [self sizeThatFits:self.bounds.size];
17
18             // set frame to fit text preserving origin
19             // call super to avoid endless loop
20             [self willChangeValueForKey:@"frame"];
21             super.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, neededSize.width, neededSize.height);
22
23
24             [self didChangeValueForKey:@"frame"];
25         }
26
27         [self setNeedsDisplay];
28         [self setNeedsLayout];
29     }
30 }

分析这段代码 我们看出

CGSize neededSize = [self sizeThatFits:self.bounds.size];

super.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, neededSize.width, neededSize.height);

这两句是关键代码    设置尺寸的话应该是在这两句完成

首先第句话  得到了  neededSize(需求的尺寸  即显示自己的完整html需要的尺寸)

第二句 利用得到的neededSize 设置 super.frame  出问题 应该是在  第一句得到的 neededSize 有问题

经过NSlog 打印 neededSize 的值后  确实  是得到的neededSize.width为整个cell的 宽度 进而 设置super.frame 为整个cell的

宽度

跟踪第一句代码  进入sizeThatFits方法

 1 - (CGSize)sizeThatFits:(CGSize)size
 2 {
 3     if (size.width==0)
 4     {
 5         size.width = self.bounds.size.width;
 6     }
 7
 8     CGSize neededSize = CGSizeMake(size.width, CGRectGetMaxY(self.layoutFrame.frame) + edgeInsets.bottom);
 9
10
11     return neededSize;
12 }

我们注意到 sizeThatFits 这个方法 需要传入一个参数(CGSize)   这里面  利用这个参数size.width 作为 

DTAttributedTextContentView 的宽度  也就是用来显示html的DTAttributedTextContentView的宽度为size.width

然后根据这个宽度 以及html  计算出DTAttributedTextContentView的高度    计算高度的所有代码全部在

CGRectGetMaxY(self.layoutFrame.frame) + edgeInsets.bottom  中   

我们只需要知道   如果我们传入的size 的宽度越宽 计算出来的 高度会越小,反之亦然,最后得到的neededSize 包含了我们规定的宽度  以及计算的高度

在这里NSLog size.width 发现 传入的宽度是 整个cell的宽度

回到上一层方法relayoutText 

CGSize neededSize = [self sizeThatFits:self.bounds.size];   传入的值是 self.bounds.size

NSlog后确实 self.bounds.size.width 为整个cell的宽度

所以得到的 neededSize  宽度为整个cell的宽度  

最后设置super.frame  的时候 便充满了整个cell


解决办法:CGSize neededSize = [self sizeThatFits:self.bounds.size];这一句代码传入正确的size  可以去storyboard中看一下 自己设置的 DTAttributedTextContentView 的宽度  以及高度  然后创建一个size 传入 方法中

最后要注意的一点

super.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, neededSize.width, neededSize.height);

改为

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

方法中的self.frame.origin.x =0;  所以 要想实现  我们设计的效果  这里不能为0   为0的话会紧挨着右边   比如cell宽度

为1024    DTAttributedTextContentView 的宽度为 824   那么 用200  替代self.frame.origin.x即可

修改后的代码

 1 - (void)relayoutText
 2 {
 3     // Make sure we actually have a superview before attempting to relayout the text.
 4     if (self.superview) {
 5         // need new layouter
 6         self.layouter = nil;
 7         self.layoutFrame = nil;
 8         //NSLog(@"%f",self.frame.size.width);
 9         // remove all links because they might have merged or split
10         [self removeAllCustomViewsForLinks];
11
12         if (_attributedString)
13         {
14             // triggers new layout
15             CGSize size=CGSizeMake(612, 44);
16
17             CGSize neededSize = [self sizeThatFits:size];
18
19             // set frame to fit text preserving origin
20             // call super to avoid endless loop
21             [self willChangeValueForKey:@"frame"];
22
23             self.frame=CGRectMake(156, self.frame.origin.y, neededSize.width, neededSize.height);
24
25             [self didChangeValueForKey:@"frame"];
26         }
27
28         [self setNeedsDisplay];
29         [self setNeedsLayout];
30     }
31 }

这样便实现了我们的需求

截至到目前分析的这么多方法  只是从

1 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
2 {
3     MyCell *cell = (MyCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];
4
5     return  cell.attributedTextContextView.frame.size.height+10;
6
7     //return [cell requiredRowHeightInTableView:tableView];
8 }

中第一句代码一路调用 深入分析的

当第一句代码最后深入调用结束  回到这里的时候 很显然  我们已经计算出了neededSize 并且将

DTAttributedTextContentView 的frame的大小也设置为合适的大小  然后  下一句

return cell.attributedTextContextView.frame.size.height+10;

通过直接读取 便可获得合适的高度给cell

到目前为止  甚至没有执行

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

方法 但是 已经把所有的html显示了一遍

当tableView 运行到

1 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
2 {
3     MyCell *cell = (MyCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];
4
5     return cell;
6 }

方法时   与heightForRowAtIndexPath:  方法是记本相同的   只不过 这次在

MyCell *cell = (MyCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];

方法中 cell不是创建出来的  而是直接从缓存中提取 的  这也是为什么  不会出现加载延迟的原因

原创博文,未经作者允许,不允许转载

转载于:https://www.cnblogs.com/bucengyongyou/archive/2012/09/02/2668006.html

原DTCoreText学习(三)-自定义DTAttributedTextCell相关推荐

  1. 修改meta标签 查看源码没效果怎么办_Spring 源码学习(三)-自定义标签

    又来填坑啦,上一篇讲完默认标签的解析,这篇笔记记录一下自定义标签的解析吧. 我们知道,Spring 源码的核心模块是 Spring-core 和 Spring-beans,在此基础上衍生出其他模块,例 ...

  2. Struts入门学习(三)---自定义类型转换器

    类型转换器是将浏览器传递的参数进行转换为了与服务器端的参数匹配,先举个例子如果我们想往服务器传递日期类型的参数时我们要怎么让浏览器传过去的让服务器明白 我们新建一个类 ConverterTest.ja ...

  3. 项目管理工具project软件学习(三) - 自定义日历【6天工作日】/【大小周】

    1. 设置6天工作日,点击[项目->更改工作时间],点击新建日历,填写名称 新建基准日历: 就是正常五天八小时工作日 复制: 选择建立的日期模板,就可以把之前的建的日历带过来 点击确定后,可以看 ...

  4. spring security 学习三-rememberMe

    spring security 学习三-rememberMe 功能:登录时的"记住我"功能 原理: rememberMeAuthenticationFilter在security过 ...

  5. PyTorch框架学习七——自定义transforms方法

    PyTorch框架学习七--自定义transforms方法 一.自定义transforms注意要素 二.自定义transforms步骤 三.自定义transforms实例:椒盐噪声 虽然前面的笔记介绍 ...

  6. iOS学习笔记-自定义过渡动画

    代码地址如下: http://www.demodashi.com/demo/11678.html 这篇笔记翻译自raywenderlick网站的过渡动画的一篇文章,原文用的swift,由于考虑到swi ...

  7. 深度学习“三巨头”、图灵奖得主 Yann LeCun:我没有天赋,所以才追随聪明人...

    Yann LeCun 作者 | 胡巍巍 出品 | 程序人生(ID:coder_life) "彻头彻尾的骗局." 2018年初,历史上首个获得公民身份的机器人索菲亚,又是上节目,又是 ...

  8. laravel框架学习(三)

    接着一套增删改查之后再学习一下自定义文件上传类实现文件上传下载 /public/uploads 文件上传位置 /app/Org/Upload.php 自定义文件上传类 1 <?php 2 //自 ...

  9. 昨日种种已得奖,那深度学习三巨头今天在忙什么?

    上周,AI圈最大的事情,没有之一,就是图灵奖,终于终于,终于颁给了深度学习三巨头. 关于Geoffrey Hinton和他的两位学生Yoshua Bengio.Yann LeCun的故事,在消息出来后 ...

最新文章

  1. shell脚本中常见的一些特殊符号和作用详解
  2. TeeChart.Direct2D.dll的使用
  3. 吴恩达深度学习笔记9-Course3-Week2【机器学习策略(ML Strategy)2】
  4. 爱可可推荐!关于竞赛思路,方法和代码实践,数据竞赛Baseline开源分享!
  5. intellij导入scala工程不识别scala语言
  6. pagefile.sys巨型文件在windows10下的尺寸调整
  7. 树莓派使用神经计算棒1代的准备(Tensorflow)
  8. UDT源码剖析(五):UDT::cleanup()过程代码注释
  9. python降维之时间类型数据的处理_使用Python进行数据降维|线性降维
  10. 术语html的含义是,术语html指的是什么
  11. innodb 共享表空间 转 独立表空间 详细说明
  12. Mac上如何重启或结束Finder进程?
  13. 【Love2d从青铜到王者】第十二篇:Love2d之碰撞检测(Detecting collision)
  14. 日渐临近的苹果秋季发布会,iOS 11 GM 固件到底提前泄露了哪些秘密?
  15. 引用变量和对象--作为初学者的混淆
  16. Word中替换手动换行符的处理
  17. 基于STM32的TLC2543驱动程序
  18. Linux sed在某行前一行和后一行添加内容
  19. 2020DCIC智慧海洋建设算法赛学习01-赛题北京及地理数据分析常用工具
  20. 6.商品服务-API-三级分类

热门文章

  1. LeetCode(476)——数字的补数(JavaScript)
  2. 百度地图API的使用教程以及案例
  3. jQuery学习(三)—jQuery使用步骤以及注意事项
  4. am最新版安装包_am软件下载-am剪辑软件(alight motion)中文版下载v2.3.1-11玩家网
  5. 作业帮电脑版在线使用_应届生应聘作业帮的在线辅导老师
  6. 当一个人把你所有联系方式删除,意味着什么?
  7. 多么痛的领悟!差不多2015年的时候,我开始关注股票
  8. 人不能轻易暴露自己的底牌,否则会陷入被动的局面
  9. 创业失败感悟第二十九天
  10. 一个人如果控制不住自己乱消费,等同于废物