本文的代码例子 : "Cell行高自适应.zip" http://vdisk.weibo.com/s/Gb9Mt
在阅读本文之前要先学会JSON数据如何进行解析以及解析后的分类,如果对JSON解析不熟的话请先阅读下面这篇文章:http://blog.csdn.net/qqmcy/article/details/9196399在这篇文章中介绍了需要的知识。
下面我们来看看代码。我需要一个第三方库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
#import <Foundation/Foundation.h>
#import "Status.h"
@interface Algorithm : NSObject
-(CGFloat)cellHeight:(NSString*)contentText with:(CGFloat)with;
//计算Cell的高度
-(CGFloat)calculation:(NSIndexPath*)indexPath StatusArr:(NSMutableArray*)statusArr;
@end
Algorithm.m
#import "Algorithm.h"
#define kTextViewPadding 16.0
#define kLineBreakMode1 UILineBreakModeWordWrap
@implementation Algorithm
-(CGFloat)calculation:(NSIndexPath *)indexPath StatusArr:(NSMutableArray *)statusArr
{
NSInteger row = indexPath.row;
if (row >= [statusArr count]) {
return 1;
}
Status* status = [statusArr objectAtIndex:row];
NSString* url = status.retweetedStatus.thumbnailPic;
NSString* url2 = status.thumbnailPic;
CGFloat height = 0.0f;
if (status.retweetedStatus && ![status.retweetedStatus isEqual:[NSNull null]]) {
//调用下面-(CGFloat)cellHeight:(NSString *)contentText with:(CGFloat)with的方法计算一段文字所占的高度
height = [self cellHeight:status.text with:320.0f] + [self cellHeight:[NSString stringWithFormat:@"%@:%@",status.retweetedStatus.user.screenName,status.retweetedStatus.text] with:300.0f] - 22.0f;
NSLog(@"status.retweetedStatus,height = %f",height);
}
else
{
height = [self cellHeight:status.text with:320.0f];
//后加调式用
height+=40;
NSLog(@"height = %f",height);
}
if ((url && [url length] != 0) || (url2 && [url2 length] != 0)) {
//如果有图片将高度增加80个像素
height = height + 80;
NSLog(@"height = %f",height);
}
return height;
}
//计算一段文字的高度
-(CGFloat)cellHeight:(NSString *)contentText with:(CGFloat)with
{
UIFont* font = [UIFont systemFontOfSize:14];
//sizeWithFont:字体大小constrainedToSize:显示的范围lineBreakMode:
CGSize size = [contentText sizeWithFont:font constrainedToSize:CGSizeMake(with - kTextViewPadding , 3000000.f) lineBreakMode:kLineBreakMode1];
CGFloat height = size.height + 44;
return height;
}
接下来我们创建一个继承自UITableViewCell
StatusCell.h
#import <UIKit/UIKit.h>
#import "Status.h"
#import "EGOImageView.h"
@interface StatusCell : UITableViewCell
@property (nonatomic, retain) UITableViewCell* tableViewCell;
@property (nonatomic, retain) UITableView* tableVw;
@property (nonatomic, retain) NSMutableArray* StatusArr;
@property (retain, nonatomic) UILabel *countLB; //数量
@property (retain, nonatomic) EGOImageView *avatarImage;
@property (retain, nonatomic) UITextView *contentTF; //微博正文
@property (retain, nonatomic) UILabel *userNameLB;
@property (retain, nonatomic) UIImageView *bgImage; //微博背景
@property (retain, nonatomic) EGOImageView *contentImage; //正文的图片
@property (retain, nonatomic) UIView *retwitterMainV; //转发的View
@property (retain, nonatomic) EGOImageView *retwitterBgImage; //转发的背景
@property (retain, nonatomic) UITextView *retwitterContentTF;//转发的正文
@property (retain, nonatomic) EGOImageView *retwitterContentImage;//转发正文的图片
//@property (assign, nonatomic) id<StatusViewCellDelegate> delegate;
@property (retain, nonatomic) NSIndexPath *cellIndexPath;
@property (retain, nonatomic) UILabel *fromLB;
@property (retain, nonatomic) UILabel *timeLB;
@property (retain, nonatomic) UILabel* sourceLB; //微博来源
-(CGFloat)setTFHeightWithImage:(BOOL)hasImage haveRetwitterImage:(BOOL)haveRetwitterImage;
-(void)setupCell:(Status*)status;
-(void)controlPosition;
@end
StatusCell.m
本文中CELL中的控件全部用代码实现,所以下面的代码比较多。
#define IMAGE_VIEW_HEIGHT 80.0f
#import "StatusCell.h"
@implementation StatusCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
//点击Cell时背景不变蓝
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.bgImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"table_header_bg.png"]];
self.bgImage.frame = CGRectMake(0, 29 , 320, 73);
// self.bgImage.contentMode = UIViewContentModeScaleToFill;
[self.contentView addSubview:self.bgImage];
self.avatarImage = [[EGOImageView alloc] initWithFrame: CGRectMake(5.0 , 5.0, 22, 22)];
self.avatarImage.image = [UIImage imageNamed:@"loadingImage_50x118.png"];
self.avatarImage.contentMode = UIViewContentModeScaleToFill;
[self.contentView addSubview:self.avatarImage];
self.userNameLB = [[UILabel alloc] initWithFrame:CGRectMake(35, 0, 165, 28)];
self.userNameLB.font = [UIFont systemFontOfSize:17.0];
self.userNameLB.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.userNameLB];
self.contentTF = [[UITextView alloc] init];
self.contentTF.editable = NO;
self.contentTF.frame = CGRectMake(0, 29 , 320, 73);
self.contentTF.font = [UIFont systemFontOfSize:14.0];
//UITextView如果有网址可以直接打开
self.contentTF.dataDetectorTypes = UIDataDetectorTypeLink;
//开启多点触摸
self.contentTF.multipleTouchEnabled = YES;
self.contentTF.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.contentTF];
self.contentImage = [[EGOImageView alloc] initWithImage:[UIImage imageNamed:@"loadingImage_50x118.png"]];
self.contentImage.frame = CGRectMake(90, 84, 140, 80);
self.contentImage.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.contentImage];
self.retwitterMainV = [[UIView alloc] initWithFrame:CGRectMake(0, 166, 320, 160)];
self.retwitterMainV.contentMode = UIViewContentModeScaleAspectFill;
self.retwitterMainV.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.retwitterMainV];
//转发背景图
self.retwitterBgImage = [[EGOImageView alloc] initWithFrame:CGRectMake(-10, -10, 320, 100)];
self.retwitterBgImage.contentMode = UIViewContentModeScaleToFill;
self.retwitterBgImage.backgroundColor = [UIColor clearColor];
[self.retwitterMainV addSubview:self.retwitterBgImage];
//转发的正文
self.retwitterContentTF = [[UITextView alloc] initWithFrame:CGRectMake(10, 5, 300, 150)];
self.retwitterContentTF.editable = NO;
self.retwitterContentTF.backgroundColor = [UIColor clearColor];
[self.retwitterMainV addSubview:self.retwitterContentTF];
//转发的图片
self.retwitterContentImage = [[EGOImageView alloc] initWithFrame:CGRectMake(90, 100, 140, 80)];
self.retwitterContentImage.backgroundColor = [UIColor clearColor];
self.retwitterContentImage.contentMode = UIViewContentModeScaleAspectFit;
[self.retwitterMainV addSubview:self.retwitterContentImage];
self.countLB = [[UILabel alloc] init];
self.countLB.backgroundColor = [UIColor clearColor];
[self.countLB setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:13.0]];
self.countLB.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin;
[self.contentView addSubview:self.countLB];
self.sourceLB = [[UILabel alloc] init];
self.sourceLB.backgroundColor = [UIColor clearColor];
[self.sourceLB setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:13.0]];
//每个控件几乎都有autoresizingMask的属性,该属性的作用是将改控件固定在相对的某个位置,例如:要将位置固定在当前cell的左下方,需要按下面的格式写UIViewAutoresizingFlexibleTopMargin代表是上方、UIViewAutoresizingFlexibleRightMargin是右方的意思。因此写代码设置控件的相对位置需要写出相反的方向。
self.sourceLB.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleRightMargin;
[self.contentView addSubview:self.sourceLB];
self.timeLB = [[UILabel alloc] initWithFrame:CGRectMake(200, 0, 165, 28)];
[self.timeLB setFont:[UIFont fontWithName:@"Arial Rounded MT Bold" size:13.0]];
self.timeLB.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.timeLB];
}
return self;
}
-(void)setupCell:(Status *)status
{
//头像图片的网址
NSURL* avatarImageUrl = [NSURL URLWithString:status.user.profileImageUrl];
//正文图片的网址
NSURL* contentImageUrl = [NSURL URLWithString:status.thumbnailPic];
//转发图片的方法
NSURL* retweetedStatusUrl = [NSURL URLWithString:status.retweetedStatus.thumbnailPic];
self.contentTF.text = status.text;
self.userNameLB.text = status.user.screenName;
self.timeLB.text = [status timestamp];
self.avatarImage.imageURL =avatarImageUrl;
self.countLB.text = [NSString stringWithFormat:@"评论:%d 转发:%d",status.commentsCount,status.retweetsCount];
self.fromLB.text = [NSString stringWithFormat:@"来自:%@",status.source];
self.timeLB.text = status.timestamp;
self.sourceLB.text = [NSString stringWithFormat:@"来源:%@",status.source];
Status *retwitterStatus = status.retweetedStatus;
//有转发
if (retwitterStatus && ![retwitterStatus isEqual:[NSNull null]])
{
self.retwitterMainV.hidden = NO;
self.retwitterContentTF.text = [NSString stringWithFormat:@"%@:%@",status.retweetedStatus.user.screenName,retwitterStatus.text];
self.contentImage.hidden = YES;
if (![retweetedStatusUrl isEqual:[NSNull null]])
{
self.retwitterContentImage.imageURL = retweetedStatusUrl;
}
NSString *url = status.retweetedStatus.thumbnailPic;
self.retwitterContentImage.hidden = url != nil && [url length] != 0 ? NO : YES;
[self setTFHeightWithImage:NO
haveRetwitterImage:url != nil && [url length] != 0 ? YES : NO];//计算cell的高度,以及背景图的处理
}
//无转发
else
{
self.retwitterMainV.hidden = YES;
if (![contentImageUrl isEqual:[NSNull null]]) {
self.contentImage.imageURL = contentImageUrl;
}
NSString *url = status.thumbnailPic;
self.contentImage.hidden = url != nil && [url length] != 0 ? NO : YES;
[self setTFHeightWithImage:url != nil && [url length] != 0 ? YES : NO
haveRetwitterImage:NO];//计算cell的高度,以及背景图的处理
}
}
//计算cell的高度,以及背景图的处理
-(CGFloat)setTFHeightWithImage:(BOOL)hasImage haveRetwitterImage:(BOOL)haveRetwitterImage
{
[self.contentTF layoutIfNeeded];
//博文Text
CGRect frame = self.contentTF.frame;
frame.size = self.contentTF.contentSize;
self.contentTF.frame = frame;
//设置正文的背景
self.bgImage.frame = frame;
//转发博文Text
frame = self.retwitterContentTF.frame;
frame.size = self.retwitterContentTF.contentSize;
self.retwitterContentTF.frame = frame;
//调整转发背景
self.retwitterBgImage.image = [[UIImage imageNamed:@"timeline_rt_border_t.png"] stretchableImageWithLeftCapWidth:130 topCapHeight:7];
frame.origin.x -=10;
frame.origin.y -=5;
frame.size.width = frame.size.width +15;
self.retwitterBgImage.frame = frame;
//转发的主View
frame = self.retwitterMainV.frame;
if (haveRetwitterImage) frame.size.height = self.retwitterContentTF.frame.size.height + IMAGE_VIEW_HEIGHT + 15;
else frame.size.height = self.retwitterContentTF.frame.size.height + 15;
if(hasImage) frame.origin.y = self.contentTF.frame.size.height + self.contentTF.frame.origin.y + IMAGE_VIEW_HEIGHT;
else frame.origin.y = self.contentTF.frame.size.height + self.contentTF.frame.origin.y;
self.retwitterMainV.frame = frame;
//转发的图片
frame = self.retwitterContentImage.frame;
frame.origin.y = self.retwitterContentTF.frame.size.height;
frame.size.height = IMAGE_VIEW_HEIGHT;
self.retwitterContentImage.frame = frame;
//正文的图片
frame = self.contentImage.frame;
frame.origin.y = self.contentTF.frame.size.height + self.contentTF.frame.origin.y - 5.0f;
frame.size.height = IMAGE_VIEW_HEIGHT;
self.contentImage.frame = frame;
//背景设置
self.bgImage.image = [[UIImage imageNamed:@"table_header_bg.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];
// = self.retwitterContentTF.frame;
return self.contentTF.contentSize.height;
}
-(void)controlPosition
{
self.countLB.frame = CGRectMake(198, self.frame.size.height - 20, 142, 21);
self.sourceLB.frame = CGRectMake(20, self.frame.size.height - 20, 152, 21);
}
再创建一个继承自的UIViewController类ShowDataViewController这个类用来创建tableView以及显示Cell中的内容。
ShowDataViewController.h
#import <UIKit/UIKit.h>
#import "Algorithm.h"
@interface ShowDataViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
//创建一个UITableView
@property (strong, nonatomic) UITableView* tb;
//Algorithm类的作用是根据内容计算Cell的高度
@property (strong, nonatomic) Algorithm* algorithm;
//用一个NSMutableArray接收Status类的对象
@property (strong , nonatomic) NSMutableArray* statusArr;
@end
ShowDataViewController.m
-(id)init
{
if (self = [super init]) {
//为什么要在init方法中注册消息中心,这样可以在AppDelegate方法中初始化这个类时注册消息中心
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getArray:) name:@"getArray" object:nil];
}
return self;
}
-(void)getArray:(NSNotification*)aNotification
{
//接收传过来的数组,数组中存有Status类的对象
self.statusArr = aNotification.object;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.tb = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 548) style:UITableViewStylePlain];
self.tb.delegate = self;
self.tb.dataSource = self;
[self.view addSubview:self.tb];
self.algorithm = [[Algorithm alloc] init];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.statusArr count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//调用Algorithm类的方法计算cell的高度 这个方法如何实现见Algorithm类中的注释
CGFloat height = [self.algorithm calculation:indexPath StatusArr:self.statusArr];
return height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
int row = indexPath.row;
static NSString* strIdentifier = @"StatusCell";
//
// //自定义Cell只需要将初始化的类变成自定义的Cell
StatusCell* cell = [tableView dequeueReusableCellWithIdentifier:strIdentifier];
if (cell == nil)
{
cell = [[StatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strIdentifier];
}
//下面这个方法是用来调整控件坐标的
[cell controlPosition];
//Status是储存微博信息的类,将数组中的微博类的对象按照行号取出
Status* status = [self.statusArr objectAtIndex:row];
NSLog(@"row = %d",row);
//下面这个方法是将微博信息放在Cell中
[cell setupCell:status];
return cell;
}
接下来我们在ViewController类中添加如下代码
ViewController.h
@interface ViewController : UIViewController
-(IBAction)changeVC:(id)sender;
-(IBAction)sendData:(id)sender;
@end
-(IBAction)sendData:(id)sender
{
//将JSON格式的数据文件的路径找出
NSString* path = [[NSBundle mainBundle] pathForResource:@"jsonTest" ofType:@"json"];
//将数据放入NSData中
NSData* data = [NSData dataWithContentsOfFile:path];
//JSON解析
NSDictionary* dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
// NSLog(@"dic = %@",dic);
NSArray* arr = [dic objectForKey:@"statuses"];
NSLog(@"%d",arr.count);
NSLog(@"arr = %@",arr);
//初始化一个数组
NSMutableArray* tempArr = [NSMutableArray array];
//将数组中的字典放入Status模型中,大家在这里会有一个疑问将数组中的字典都放入模型中,怎么区分不同数组中的数据?
for (NSDictionary* item in arr) {
//答案在statusWithJsonDictionary:的类方法中见该方法注释
Status* status = [Status statusWithJsonDictionary:item];
//将Status类型的对象放入数组中
[tempArr addObject:status];
}
//将tempArr数组通过消息中心发送到@"getArray" 这个名字的消息对应的方法中
[[NSNotificationCenter defaultCenter] postNotificationName:@"getArray" object:tempArr];
}
-(IBAction)changeVC:(id)sender
{
//切换视图控制器
[[NSNotificationCenter defaultCenter] postNotificationName:@"changeVC" object:nil];
}
我们还要在AppDelegate类中添加如下代码。
#import <UIKit/UIKit.h>
#import "ShowDataViewController.h"
@class ViewController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController *viewController;
@property (strong, nonatomic) ShowDataViewController* showVC;
@end
AppDelegate.m
#import "AppDelegate.h"
#import "ShowDataViewController.h"
#import "ViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//为了注册消息
[[ShowDataViewController alloc] init];
//注册消息为了切换视图控制器
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeVC) name:@"changeVC" object:nil];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
self.showVC = [[ShowDataViewController alloc] init];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
-(void)changeVC
{
self.window.rootViewController = self.showVC;
}