iOS中Cell高度如何能够自动适应需要显示的内容

时间:2023-12-04 12:50:08

本文的代码例子 : "Cell行高自适应.zip" http://vdisk.weibo.com/s/Gb9Mt

下面我们来看看代码。我需要一个第三方库EGO异步下载、addtion文件夹和StringUtil文件以及Comment、Status、User这三个数据模型,这篇文章的主要目的是讲解如何计算Cell的高度,jSON数据分类见上面那篇文章,上面说的在代码例子中都有的。将它们考入你的工程。

实现思路:

/*

File.strings

Cell行高自适应

Created by 杜甲 on 13-4-2.

Copyright (c) 2013年杜甲. All rights reserved.

实现类似微博这样的项目需要怎么做?

1、首先,要获取数据。(这部分在网络请求中介绍)

2、其次,将数据分类保存在对应的模型中。(这个技术在大字典中介绍)

3、最后,将获取的数据显示在屏幕上。

一、为什么要学习Cell的行高自适应?

因为,我们要将获取的内容合理的显示在屏幕上。

二、实现步骤

1、要先将数据发送到显示界面的类ShowDataViewController中

2、计算获取每个数据单元需要在屏幕上占多大的空间,这个功能的实现在类Algorithm中,详细解释在类中。

3、创建一个UITableViewCell类型的类:StatusCell这个类的作用就是为了设计如何在Cell中显示数据进行布局。

三、技术难点:

1、控件.autoresizingMask属性:每个控件几乎都有autoresizingMask的属性,该属性的作用是将改控件固定在相对的某个位置,例如:要将位置固定在当前cell的左下方,需要按下面的格式写UIViewAutoresizingFlexibleTopMargin代表是上方、UIViewAutoresizingFlexibleRightMargin是右方的意思。因此写代码设置控件的相对位置需要写出相反的方向。

*/

我们先实现一个计算Cell高度的类:

Algorithm.h

  1. <span >#import <Foundation/Foundation.h>
  2. #import "Status.h"
  3. @interface Algorithm : NSObject
  4. -(CGFloat)cellHeight:(NSString*)contentText with:(CGFloat)with;
  5. //计算Cell的高度
  6. -(CGFloat)calculation:(NSIndexPath*)indexPath StatusArr:(NSMutableArray*)statusArr;
  7. @end
  8. </span>

Algorithm.m

  1. <span >#import "Algorithm.h"
  2. #define kTextViewPadding            16.0
  3. #define kLineBreakMode1              UILineBreakModeWordWrap
  4. @implementation Algorithm
  5. -(CGFloat)calculation:(NSIndexPath *)indexPath StatusArr:(NSMutableArray *)statusArr
  6. {
  7. NSInteger row = indexPath.row;
  8. if (row >= [statusArr count]) {
  9. return 1;
  10. }
  11. Status* status = [statusArr objectAtIndex:row];
  12. NSString* url = status.retweetedStatus.thumbnailPic;
  13. NSString* url2 = status.thumbnailPic;
  14. CGFloat height  = 0.0f;
  15. if (status.retweetedStatus && ![status.retweetedStatus isEqual:[NSNull null]]) {
  16. //调用下面-(CGFloat)cellHeight:(NSString *)contentText with:(CGFloat)with的方法计算一段文字所占的高度
  17. height = [self cellHeight:status.text with:320.0f] + [self cellHeight:[NSString stringWithFormat:@"%@:%@",status.retweetedStatus.user.screenName,status.retweetedStatus.text] with:300.0f] - 22.0f;
  18. NSLog(@"status.retweetedStatus,height = %f",height);
  19. }
  20. else
  21. {
  22. height = [self cellHeight:status.text with:320.0f];
  23. //后加调式用
  24. height+=40;
  25. NSLog(@"height = %f",height);
  26. }
  27. if ((url && [url length] != 0) || (url2 && [url2 length] != 0)) {
  28. //如果有图片将高度增加80个像素
  29. height = height + 80;
  30. NSLog(@"height = %f",height);
  31. }
  32. return height;
  33. }</span>
  1. <span >//计算一段文字的高度
  2. -(CGFloat)cellHeight:(NSString *)contentText with:(CGFloat)with
  3. {
  4. UIFont* font = [UIFont systemFontOfSize:14];
  5. //sizeWithFont:字体大小constrainedToSize:显示的范围lineBreakMode:
  6. CGSize size = [contentText sizeWithFont:font constrainedToSize:CGSizeMake(with - kTextViewPadding , 3000000.f) lineBreakMode:kLineBreakMode1];
  7. CGFloat height = size.height + 44;
  8. return height;
  9. }
  10. </span>

接下来我们创建一个继承自UITableViewCell

StatusCell.h

  1. <span >#import <UIKit/UIKit.h>
  2. #import "Status.h"
  3. #import "EGOImageView.h"
  4. @interface StatusCell : UITableViewCell
  5. @property (nonatomic, retain) UITableViewCell* tableViewCell;
  6. @property (nonatomic, retain) UITableView*     tableVw;
  7. @property (nonatomic, retain) NSMutableArray* StatusArr;
  8. @property (retain, nonatomic)  UILabel *countLB;             //数量
  9. @property (retain, nonatomic)  EGOImageView *avatarImage;
  10. @property (retain, nonatomic)  UITextView *contentTF;        //微博正文
  11. @property (retain, nonatomic) UILabel *userNameLB;
  12. @property (retain, nonatomic)  UIImageView *bgImage;         //微博背景
  13. @property (retain, nonatomic)  EGOImageView *contentImage;  //正文的图片
  14. @property (retain, nonatomic)  UIView *retwitterMainV;       //转发的View
  15. @property (retain, nonatomic)  EGOImageView *retwitterBgImage;     //转发的背景
  16. @property (retain, nonatomic)  UITextView *retwitterContentTF;//转发的正文
  17. @property (retain, nonatomic)  EGOImageView *retwitterContentImage;//转发正文的图片
  18. //@property (assign, nonatomic) id<StatusViewCellDelegate> delegate;
  19. @property (retain, nonatomic) NSIndexPath *cellIndexPath;
  20. @property (retain, nonatomic)  UILabel *fromLB;
  21. @property (retain, nonatomic)  UILabel *timeLB;
  22. @property (retain, nonatomic)  UILabel* sourceLB;  //微博来源
  23. -(CGFloat)setTFHeightWithImage:(BOOL)hasImage haveRetwitterImage:(BOOL)haveRetwitterImage;
  24. -(void)setupCell:(Status*)status;
  25. -(void)controlPosition;
  26. @end
  27. </span>

StatusCell.m

本文中CELL中的控件全部用代码实现,所以下面的代码比较多。

  1. <span >#define IMAGE_VIEW_HEIGHT 80.0f
  2. #import "StatusCell.h"
  3. @implementation StatusCell
  4. - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  5. {
  6. self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  7. if (self) {
  8. // Initialization code
  9. //点击Cell时背景不变蓝
  10. self.selectionStyle = UITableViewCellSelectionStyleNone;
  11. self.bgImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"table_header_bg.png"]];
  12. self.bgImage.frame = CGRectMake(0, 29 , 320, 73);
  13. // self.bgImage.contentMode = UIViewContentModeScaleToFill;
  14. [self.contentView addSubview:self.bgImage];
  15. self.avatarImage = [[EGOImageView alloc] initWithFrame: CGRectMake(5.0 , 5.0, 22, 22)];
  16. self.avatarImage.image = [UIImage imageNamed:@"loadingImage_50x118.png"];
  17. self.avatarImage.contentMode = UIViewContentModeScaleToFill;
  18. [self.contentView addSubview:self.avatarImage];
  19. self.userNameLB = [[UILabel alloc] initWithFrame:CGRectMake(35, 0, 165, 28)];
  20. self.userNameLB.font = [UIFont systemFontOfSize:17.0];
  21. self.userNameLB.backgroundColor = [UIColor clearColor];
  22. [self.contentView addSubview:self.userNameLB];
  23. self.contentTF = [[UITextView alloc] init];
  24. self.contentTF.editable = NO;
  25. self.contentTF.frame = CGRectMake(0, 29 , 320, 73);
  26. self.contentTF.font = [UIFont systemFontOfSize:14.0];
  27. //UITextView如果有网址可以直接打开
  28. self.contentTF.dataDetectorTypes = UIDataDetectorTypeLink;
  29. //开启多点触摸
  30. self.contentTF.multipleTouchEnabled = YES;
  31. self.contentTF.backgroundColor = [UIColor clearColor];
  32. [self.contentView addSubview:self.contentTF];
  33. self.contentImage = [[EGOImageView alloc] initWithImage:[UIImage imageNamed:@"loadingImage_50x118.png"]];
  34. self.contentImage.frame = CGRectMake(90, 84, 140, 80);
  35. self.contentImage.backgroundColor = [UIColor clearColor];
  36. [self.contentView addSubview:self.contentImage];
  37. self.retwitterMainV = [[UIView alloc] initWithFrame:CGRectMake(0, 166, 320, 160)];
  38. self.retwitterMainV.contentMode = UIViewContentModeScaleAspectFill;
  39. self.retwitterMainV.backgroundColor = [UIColor clearColor];
  40. [self.contentView addSubview:self.retwitterMainV];
  41. //转发背景图
  42. self.retwitterBgImage = [[EGOImageView alloc] initWithFrame:CGRectMake(-10, -10, 320, 100)];
  43. self.retwitterBgImage.contentMode = UIViewContentModeScaleToFill;
  44. self.retwitterBgImage.backgroundColor = [UIColor clearColor];
  45. [self.retwitterMainV addSubview:self.retwitterBgImage];
  46. //转发的正文
  47. self.retwitterContentTF = [[UITextView alloc] initWithFrame:CGRectMake(10, 5, 300, 150)];
  48. self.retwitterContentTF.editable = NO;
  49. self.retwitterContentTF.backgroundColor = [UIColor clearColor];
  50. [self.retwitterMainV addSubview:self.retwitterContentTF];
  51. //转发的图片
  52. self.retwitterContentImage = [[EGOImageView alloc] initWithFrame:CGRectMake(90, 100, 140, 80)];
  53. self.retwitterContentImage.backgroundColor = [UIColor clearColor];
  54. self.retwitterContentImage.contentMode = UIViewContentModeScaleAspectFit;
  55. [self.retwitterMainV addSubview:self.retwitterContentImage];
  56. self.countLB = [[UILabel alloc] init];
  57. self.countLB.backgroundColor = [UIColor clearColor];
  58. [self.countLB setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:13.0]];
  59. self.countLB.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin;
  60. [self.contentView addSubview:self.countLB];
  61. self.sourceLB = [[UILabel alloc] init];
  62. self.sourceLB.backgroundColor = [UIColor clearColor];
  63. [self.sourceLB setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:13.0]];
  64. //每个控件几乎都有autoresizingMask的属性,该属性的作用是将改控件固定在相对的某个位置,例如:要将位置固定在当前cell的左下方,需要按下面的格式写UIViewAutoresizingFlexibleTopMargin代表是上方、UIViewAutoresizingFlexibleRightMargin是右方的意思。因此写代码设置控件的相对位置需要写出相反的方向。
  65. self.sourceLB.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleRightMargin;
  66. [self.contentView addSubview:self.sourceLB];
  67. self.timeLB = [[UILabel alloc] initWithFrame:CGRectMake(200, 0, 165, 28)];
  68. [self.timeLB setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:13.0]];
  69. self.timeLB.backgroundColor = [UIColor clearColor];
  70. [self.contentView addSubview:self.timeLB];
  71. }
  72. return self;
  73. }</span>
  1. <span >-(void)setupCell:(Status *)status
  2. {
  3. //头像图片的网址
  4. NSURL* avatarImageUrl = [NSURL URLWithString:status.user.profileImageUrl];
  5. //正文图片的网址
  6. NSURL* contentImageUrl = [NSURL URLWithString:status.thumbnailPic];
  7. //转发图片的方法
  8. NSURL* retweetedStatusUrl = [NSURL URLWithString:status.retweetedStatus.thumbnailPic];
  9. self.contentTF.text = status.text;
  10. self.userNameLB.text = status.user.screenName;
  11. self.timeLB.text = [status timestamp];
  12. self.avatarImage.imageURL =avatarImageUrl;
  13. self.countLB.text = [NSString stringWithFormat:@"评论:%d 转发:%d",status.commentsCount,status.retweetsCount];
  14. self.fromLB.text = [NSString stringWithFormat:@"来自:%@",status.source];
  15. self.timeLB.text = status.timestamp;
  16. self.sourceLB.text = [NSString stringWithFormat:@"来源:%@",status.source];
  17. Status  *retwitterStatus    = status.retweetedStatus;
  18. //有转发
  19. if (retwitterStatus && ![retwitterStatus isEqual:[NSNull null]])
  20. {
  21. self.retwitterMainV.hidden = NO;
  22. self.retwitterContentTF.text = [NSString stringWithFormat:@"%@:%@",status.retweetedStatus.user.screenName,retwitterStatus.text];
  23. self.contentImage.hidden = YES;
  24. if (![retweetedStatusUrl isEqual:[NSNull null]])
  25. {
  26. self.retwitterContentImage.imageURL = retweetedStatusUrl;
  27. }
  28. NSString *url = status.retweetedStatus.thumbnailPic;
  29. self.retwitterContentImage.hidden = url != nil && [url length] != 0 ? NO : YES;
  30. [self setTFHeightWithImage:NO
  31. haveRetwitterImage:url != nil && [url length] != 0 ? YES : NO];//计算cell的高度,以及背景图的处理
  32. }
  33. //无转发
  34. else
  35. {
  36. self.retwitterMainV.hidden = YES;
  37. if (![contentImageUrl isEqual:[NSNull null]]) {
  38. self.contentImage.imageURL = contentImageUrl;
  39. }
  40. NSString *url = status.thumbnailPic;
  41. self.contentImage.hidden = url != nil && [url length] != 0 ? NO : YES;
  42. [self setTFHeightWithImage:url != nil && [url length] != 0 ? YES : NO
  43. haveRetwitterImage:NO];//计算cell的高度,以及背景图的处理
  44. }
  45. }</span>
  1. <span >//计算cell的高度,以及背景图的处理
  2. -(CGFloat)setTFHeightWithImage:(BOOL)hasImage haveRetwitterImage:(BOOL)haveRetwitterImage
  3. {
  4. [self.contentTF layoutIfNeeded];
  5. //博文Text
  6. CGRect frame = self.contentTF.frame;
  7. frame.size = self.contentTF.contentSize;
  8. self.contentTF.frame = frame;
  9. //设置正文的背景
  10. self.bgImage.frame = frame;
  11. //转发博文Text
  12. frame = self.retwitterContentTF.frame;
  13. frame.size = self.retwitterContentTF.contentSize;
  14. self.retwitterContentTF.frame = frame;
  15. //调整转发背景
  16. self.retwitterBgImage.image = [[UIImage imageNamed:@"timeline_rt_border_t.png"] stretchableImageWithLeftCapWidth:130 topCapHeight:7];
  17. frame.origin.x -=10;
  18. frame.origin.y -=5;
  19. frame.size.width = frame.size.width +15;
  20. self.retwitterBgImage.frame = frame;
  21. //转发的主View
  22. frame = self.retwitterMainV.frame;
  23. if (haveRetwitterImage) frame.size.height = self.retwitterContentTF.frame.size.height + IMAGE_VIEW_HEIGHT + 15;
  24. else frame.size.height = self.retwitterContentTF.frame.size.height + 15;
  25. if(hasImage) frame.origin.y = self.contentTF.frame.size.height + self.contentTF.frame.origin.y + IMAGE_VIEW_HEIGHT;
  26. else frame.origin.y = self.contentTF.frame.size.height + self.contentTF.frame.origin.y;
  27. self.retwitterMainV.frame = frame;
  28. //转发的图片
  29. frame = self.retwitterContentImage.frame;
  30. frame.origin.y = self.retwitterContentTF.frame.size.height;
  31. frame.size.height = IMAGE_VIEW_HEIGHT;
  32. self.retwitterContentImage.frame = frame;
  33. //正文的图片
  34. frame = self.contentImage.frame;
  35. frame.origin.y = self.contentTF.frame.size.height + self.contentTF.frame.origin.y - 5.0f;
  36. frame.size.height = IMAGE_VIEW_HEIGHT;
  37. self.contentImage.frame = frame;
  38. //背景设置
  39. self.bgImage.image = [[UIImage imageNamed:@"table_header_bg.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];
  40. //   = self.retwitterContentTF.frame;
  41. return self.contentTF.contentSize.height;
  42. }</span>
  1. <span >-(void)controlPosition
  2. {
  3. self.countLB.frame = CGRectMake(198, self.frame.size.height - 20, 142, 21);
  4. self.sourceLB.frame = CGRectMake(20, self.frame.size.height - 20, 152, 21);
  5. }</span>

再创建一个继承自的UIViewController类ShowDataViewController这个类用来创建tableView以及显示Cell中的内容。

ShowDataViewController.h

  1. <span >#import <UIKit/UIKit.h>
  2. #import "Algorithm.h"
  3. @interface ShowDataViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
  4. //创建一个UITableView
  5. @property (strong, nonatomic) UITableView* tb;
  6. //Algorithm类的作用是根据内容计算Cell的高度
  7. @property (strong, nonatomic) Algorithm* algorithm;
  8. //用一个NSMutableArray接收Status类的对象
  9. @property (strong , nonatomic) NSMutableArray* statusArr;
  10. @end
  11. </span>

ShowDataViewController.m

  1. <span >-(id)init
  2. {
  3. if (self = [super init]) {
  4. //为什么要在init方法中注册消息中心,这样可以在AppDelegate方法中初始化这个类时注册消息中心
  5. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getArray:) name:@"getArray" object:nil];
  6. }
  7. return self;
  8. }</span>
  1. <span >-(void)getArray:(NSNotification*)aNotification
  2. {
  3. //接收传过来的数组,数组中存有Status类的对象
  4. self.statusArr = aNotification.object;
  5. }</span>
  1. <span >- (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. // Do any additional setup after loading the view.
  5. self.tb = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 548) style:UITableViewStylePlain];
  6. self.tb.delegate = self;
  7. self.tb.dataSource = self;
  8. [self.view addSubview:self.tb];
  9. self.algorithm = [[Algorithm alloc] init];
  10. }</span>
  1. <span >- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  2. {
  3. return [self.statusArr count];
  4. }</span>
  1. <span >-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3. //调用Algorithm类的方法计算cell的高度 这个方法如何实现见Algorithm类中的注释
  4. CGFloat height = [self.algorithm calculation:indexPath StatusArr:self.statusArr];
  5. return height;
  6. }</span>
  1. <span >- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3. int  row = indexPath.row;
  4. static NSString* strIdentifier = @"StatusCell";
  5. //
  6. //    //自定义Cell只需要将初始化的类变成自定义的Cell
  7. StatusCell* cell  = [tableView dequeueReusableCellWithIdentifier:strIdentifier];
  8. if (cell == nil)
  9. {
  10. cell =  [[StatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strIdentifier];
  11. }
  12. //下面这个方法是用来调整控件坐标的
  13. [cell controlPosition];
  14. //Status是储存微博信息的类,将数组中的微博类的对象按照行号取出
  15. Status* status = [self.statusArr objectAtIndex:row];
  16. NSLog(@"row = %d",row);
  17. //下面这个方法是将微博信息放在Cell中
  18. [cell setupCell:status];
  19. return cell;
  20. }
  21. </span>

接下来我们在ViewController类中添加如下代码

ViewController.h

  1. <span >@interface ViewController : UIViewController
  2. -(IBAction)changeVC:(id)sender;
  3. -(IBAction)sendData:(id)sender;
  4. @end
  5. </span>

自己在XIB中拖2个button与下面两个方法相关联吧

  1. <span >-(IBAction)sendData:(id)sender
  2. {
  3. //将JSON格式的数据文件的路径找出
  4. NSString* path = [[NSBundle mainBundle] pathForResource:@"jsonTest" ofType:@"json"];
  5. //将数据放入NSData中
  6. NSData* data = [NSData dataWithContentsOfFile:path];
  7. //JSON解析
  8. NSDictionary* dic =  [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
  9. //    NSLog(@"dic = %@",dic);
  10. NSArray* arr = [dic objectForKey:@"statuses"];
  11. NSLog(@"%d",arr.count);
  12. NSLog(@"arr = %@",arr);
  13. //初始化一个数组
  14. NSMutableArray* tempArr = [NSMutableArray array];
  15. //将数组中的字典放入Status模型中,大家在这里会有一个疑问将数组中的字典都放入模型中,怎么区分不同数组中的数据?
  16. for (NSDictionary* item in arr) {
  17. //答案在statusWithJsonDictionary:的类方法中见该方法注释
  18. Status* status = [Status statusWithJsonDictionary:item];
  19. //将Status类型的对象放入数组中
  20. [tempArr addObject:status];
  21. }
  22. //将tempArr数组通过消息中心发送到@"getArray" 这个名字的消息对应的方法中
  23. [[NSNotificationCenter defaultCenter] postNotificationName:@"getArray" object:tempArr];
  24. }
  25. </span>
  1. <span >-(IBAction)changeVC:(id)sender
  2. {
  3. //切换视图控制器
  4. [[NSNotificationCenter defaultCenter] postNotificationName:@"changeVC" object:nil];
  5. }
  6. </span>

我们还要在AppDelegate类中添加如下代码。

  1. <span >#import <UIKit/UIKit.h>
  2. <span >#import "ShowDataViewController.h"
  3. @class ViewController;</span><span >
  4. </span>
  5. @interface AppDelegate : UIResponder <UIApplicationDelegate>
  6. @property (strong, nonatomic) UIWindow *window;
  7. @property (strong, nonatomic) ViewController *viewController;
  8. <span >@property (strong, nonatomic) ShowDataViewController* showVC;
  9. </span>
  10. @end</span>

AppDelegate.m

  1. <span >#import "AppDelegate.h"
  2. #import "ShowDataViewController.h"
  3. <span >#import "ViewController.h"</span>
  4. @implementation AppDelegate
  5. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  6. {</span>
  1. <span ><span > </span>//为了注册消息</span>
  1. <span >   <span > [[ShowDataViewController alloc] init];</span></span>
  1. <span ><span >//注册消息为了切换视图控制器
  2. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeVC) name:@"changeVC" object:nil];</span>
  3. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  4. // Override point for customization after application launch.
  5. self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
  6. self.showVC = [[ShowDataViewController alloc] init];
  7. self.window.rootViewController = self.viewController;
  8. [self.window makeKeyAndVisible];
  9. return YES;
  10. }</span>
  1. <span >-(void)changeVC
  2. {
  3. self.window.rootViewController = self.showVC;
  4. }</span>