本文的代碼例子 : "Cell行高自適應.zhttp://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、要先將數據發(fā)送到顯示界面的類ShowDataViewController中
2、計算獲取每個數據單元需要在屏幕上占多大的空間,這個功能的實現在類Algorithm中,詳細解釋在類中。
3、創(chuàng)建一個UITableViewCell類型的類:StatusCell這個類的作用就是為了設計如何在Cell中顯示數據進行布局。
三、技術難點:
1、控件.autoresizingMask屬性:每個控件幾乎都有autoresizingMask的屬性,該屬性的作用是將改控件固定在相對的某個位置,例如:要將位置固定在當前cell的左下方,需要按下面的格式寫UIViewAutoresizingFlexibleTopMargin代表是上方、UIViewAutoresizingFlexibleRightMargin是右方的意思。因此寫代碼設置控件的相對位置需要寫出相反的方向。
*/
我們先實現一個計算Cell高度的類:
Algorithm.h
- <span >#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
- </span>
Algorithm.m
- <span >#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;
-
- }</span>
- <span >//計算一段文字的高度
- -(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;
- }
- </span>
接下來我們創(chuàng)建一個繼承自UITableViewCell
StatusCell.h
- <span >#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; //轉發(fā)的View
- @property (retain, nonatomic) EGOImageView *retwitterBgImage; //轉發(fā)的背景
- @property (retain, nonatomic) UITextView *retwitterContentTF;//轉發(fā)的正文
- @property (retain, nonatomic) EGOImageView *retwitterContentImage;//轉發(fā)正文的圖片
- //@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
- </span>
StatusCell.m
本文中CELL中的控件全部用代碼實現,所以下面的代碼比較多。
- <span >#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];
-
- //轉發(fā)背景圖
- self.retwitterBgImage = [[EGOImageView alloc] initWithFrame:CGRectMake(-10, -10, 320, 100)];
- self.retwitterBgImage.contentMode = UIViewContentModeScaleToFill;
- self.retwitterBgImage.backgroundColor = [UIColor clearColor];
- [self.retwitterMainV addSubview:self.retwitterBgImage];
-
- //轉發(fā)的正文
- self.retwitterContentTF = [[UITextView alloc] initWithFrame:CGRectMake(10, 5, 300, 150)];
- self.retwitterContentTF.editable = NO;
- self.retwitterContentTF.backgroundColor = [UIColor clearColor];
- [self.retwitterMainV addSubview:self.retwitterContentTF];
-
- //轉發(fā)的圖片
- 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;
- }</span>
- <span >-(void)setupCell:(Status *)status
- {
- //頭像圖片的網址
- NSURL* avatarImageUrl = [NSURL URLWithString:status.user.profileImageUrl];
- //正文圖片的網址
- NSURL* contentImageUrl = [NSURL URLWithString:status.thumbnailPic];
- //轉發(fā)圖片的方法
- 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 轉發(fā):%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;
-
- //有轉發(fā)
- 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的高度,以及背景圖的處理
- }
-
- //無轉發(fā)
- 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的高度,以及背景圖的處理
- }
- }</span>
- <span >//計算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;
-
- //轉發(fā)博文Text
- frame = self.retwitterContentTF.frame;
- frame.size = self.retwitterContentTF.contentSize;
- self.retwitterContentTF.frame = frame;
- //調整轉發(fā)背景
- 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;
-
- //轉發(fā)的主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;
-
-
- //轉發(fā)的圖片
- 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;
- }</span>
- <span >-(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);
-
- }</span>
再創(chuàng)建一個繼承自的UIViewController類ShowDataViewController這個類用來創(chuàng)建tableView以及顯示Cell中的內容。
ShowDataViewController.h
- <span >#import <UIKit/UIKit.h>
- #import "Algorithm.h"
- @interface ShowDataViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
- //創(chuàng)建一個UITableView
- @property (strong, nonatomic) UITableView* tb;
- //Algorithm類的作用是根據內容計算Cell的高度
- @property (strong, nonatomic) Algorithm* algorithm;
- //用一個NSMutableArray接收Status類的對象
- @property (strong , nonatomic) NSMutableArray* statusArr;
- @end
- </span>
ShowDataViewController.m
- <span >-(id)init
- {
- if (self = [super init]) {
- //為什么要在init方法中注冊消息中心,這樣可以在AppDelegate方法中初始化這個類時注冊消息中心
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getArray:) name:@"getArray" object:nil];
- }
- return self;
- }</span>
- <span >-(void)getArray:(NSNotification*)aNotification
- {
- //接收傳過來的數組,數組中存有Status類的對象
- self.statusArr = aNotification.object;
- }</span>
- <span >- (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];
-
-
-
- }</span>
- <span >- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- {
- return [self.statusArr count];
- }</span>
- <span >-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
- {
- //調用Algorithm類的方法計算cell的高度 這個方法如何實現見Algorithm類中的注釋
- CGFloat height = [self.algorithm calculation:indexPath StatusArr:self.statusArr];
-
- return height;
- }</span>
- <span >- (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;
-
- }
- </span>
接下來我們在ViewController類中添加如下代碼
ViewController.h
- <span >@interface ViewController : UIViewController
- -(IBAction)changeVC:(id)sender;
- -(IBAction)sendData:(id)sender;
- @end
- </span>
自己在XIB中拖2個button與下面兩個方法相關聯吧
- <span >-(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模型中,大家在這里會有一個疑問將數組中的字典都放入模型中,怎么區(qū)分不同數組中的數據?
- for (NSDictionary* item in arr) {
- //答案在statusWithJsonDictionary:的類方法中見該方法注釋
- Status* status = [Status statusWithJsonDictionary:item];
-
-
-
-
- //將Status類型的對象放入數組中
- [tempArr addObject:status];
- }
- //將tempArr數組通過消息中心發(fā)送到@"getArray" 這個名字的消息對應的方法中
- [[NSNotificationCenter defaultCenter] postNotificationName:@"getArray" object:tempArr];
- }
- </span>
- <span >-(IBAction)changeVC:(id)sender
- {
- //切換視圖控制器
- [[NSNotificationCenter defaultCenter] postNotificationName:@"changeVC" object:nil];
-
- }
- </span>
我們還要在AppDelegate類中添加如下代碼。
- <span >#import <UIKit/UIKit.h>
- <span >#import "ShowDataViewController.h"
- @class ViewController;</span><span >
- </span>
- @interface AppDelegate : UIResponder <UIapplicationDelegate>
-
- @property (strong, nonatomic) UIWindow *window;
-
- @property (strong, nonatomic) ViewController *viewController;
- <span >@property (strong, nonatomic) ShowDataViewController* showVC;
- </span>
- @end</span>
AppDelegate.m
- <span >#import "AppDelegate.h"
- #import "ShowDataViewController.h"
- <span >#import "ViewController.h"</span>
-
- @implementation AppDelegate
-
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {</span>
- <span ><span > </span>//為了注冊消息</span>
- <span > <span > [[ShowDataViewController alloc] init];</span></span>
- <span ><span >//注冊消息為了切換視圖控制器
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeVC) name:@"changeVC" object:nil];</span>
- 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;
- }</span>
- <span >-(void)changeVC
- {
- self.window.rootViewController = self.showVC;
- }</span>