一 自定义 cell
1.在实际编程的过程中,往往会根据不同的需求,设计出不同的界面样式,所以我们需要在系统提供的控件的基础上,自定义控件用来布局界面.
2.自定义cell 就是创建一个UITableViewCell的子类.
3.把 cell 上的控件创建都封装在子类中,简化UIViewController 中的代码
4.子视图控件添加到 cell的 contentView 上.
二 cell 中的控件显示 Model 中的信息
1. cell 中声明了一个 Model 类型的属性,viewController 中获取到 Mode 对象后赋值给 cell的 Model 属性
2. cell 中重写 Model 的 setter 方法,把 Model 对象中的内容重新赋值给各个控件
3. M和 V不直接进行通信, C负责 M和 V之间进行通信
三 cell 中的自定义控件
1. 计算文本高度是所用的字体要和 label 显示时用的字体一致
2. label 的宽度要和计算时使用的限定宽度一致
3. 这样才能保证文本显示在 label 中时, label 中时, label 高度恰巧够.
4. tableView: heightForRowAtIndexPath: 方法要比 tableView:cellForRowAtIndexPath先执行
5. 所以要提前计算号每个文本cell 需要的高度
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath{
需要一个 News对象, cellHeight是一个自定义的类方法,所有只能通过类进行调用
return [NewCellcellHeight:self:mArray[indexPath.row]];
}
6. 自定义计算文本高度的方法:
1>主要方法:
[summary boundingRectWithSize:size options:
NSStringDrawingUsesLineFragmentOriginattributes:attributes context:nil]
2>设置了 cell 的高度之后,还不能实现文本内容的自适应,需要将显示文本的 Lable 控件也设置为自适应高度,所以需要在对 Lable 赋值完成之后进行设置
(因为不能在 frame 中对高度进行修改)所以需要先取出 frame 的值,进行修改,完成之后,再重新对 frame 进行赋值(修改的时候,使用[self class]调取类方法进行操作)
7. 使用自定义的 cell的时候,使用步骤:
1> 首先需要在根视图控制器文件中引入自定义的 cell 文件;
2> 然后在视图控制器的viewDidLoad 方法中进行注册;(可以注册多个自定义cell)
[self.tableViewregisterClass: [(自定义 cell控件名称) class] forCellReuseIdentifier:@”cell” ];
3> 第三步是在创建 cell 的方法中进行赋值(在tableview:cellForRowAtIndexPath:方法中进行赋值)
① 从数组中取出存储的Student对象
Student *stu =self.mArray[indexPath.row];
② 在重用池中查找是否存在带有步骤2中”cell”标记的自定义 cell
GrilTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@”cell”forIndexPath:indexPath];
③ 将从数组中取出的数据对象赋值给自定义 cell. student 进行显示
cell.student = stu;
8. 从 plist 中读取数据,并将数据存放到数组中
1> 首先定义一个可变数组(为了方便操作,一般定义成私有属性)
@property (nonatomic,retain) NSMutableArray *mArray;
2> 从文件中读取数据
① 获取文件路径
NSString *filePath =[[NSBundle mainBundle]pathForResource:@”StudentList” ofType:@”plist”];
② 获取文件的数据(如果文件最外层是数组的话.就用数组获取;如果是字典,就使用字典获取)
NSArray *array =[NSArray arrayWithContentsOfFile:filePath];
在这里可以对读取的内容进行打印,以检测是否获取到文件的内容
NSLog(@”%@”,array);
③ 初始化定义的数组,然后才能使用.
self.mArray =[NSMutableArray arrayWithCapacity:15];
④ 遍历第二步创建的数组
for(NSDictionary *dicin array){
// 将字典中的数据封装成student对象
Student *student =[[Student alloc]init];
// 在字典中根据 key 值找到对应的 value 值并赋值给 student 对象对应的属性(是一个非常方便的方法)
[student setValuesForKeysWithDictionary:dic]
在使用上面方法的时候,问了防止取到的数据为空,而造成程序崩溃,需要在定义的 Model 文件中(也就是存储数据的文件)添加下面的方法:
// 防止没有定义 key 值时,还在为 key 的 Value 赋值造成的崩溃问题(方法内容可以不用实现)
-(void)setValue:(id)value forUndefinedKey: (NSString *)key{ }
⑤ 将封装的对象添加到数组中
[self.mArrayaddObject:student];
最后释放 student 对象
[student release];
}
3> 最后只需要在创建 cell的时候取出数组中的对象进行操作就可以了.
9. 在自定义 TableViewCell 的时候,需要重写 init 方法
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier: (NSString *)reuseIdentifier{
self = [superinitWithStyle:style reuseIdentifier: reuseIdentifier];
if(self){
[self p_setUp];
}
return self; }
10 .cell 中的控件显示 Model 中的信息
1>cell 中声明了一个 Model 类型的属性, viewController 中获取到 Mode 对象后赋值给 cell的 Model 属性
2>cell 中重写 Model 的 setter 方法,把 Model 对象中的内容重新赋值给各个控件
重写 student 属性的 setter 方法,实现两步操作
-(void)setStudent: (Student *)student{
// 1.存储数据对象
if(_student != student){
[_student release];
_student = [student retain];
}
// 2.将传入的数据赋值到 cell 子视图上显示
self.nameLable.text = student.name;
self.ageLable.text = student.age;
}
3>M和 V不直接进行通信, C负责 M和 V之间进行通信
主要代码:
1自定义 cell
#import "RootTableViewController.h"
#import "Student.h"
#import "BoyTableViewCell.h"
#import "GirlTableViewCell.h"
#define kBoyCell @"boyCell"
#define kGirlCell @"GirlCell"
@interface RootTableViewController ()
@property (nonatomic,retain) NSMutableArray *mArray;
@property (nonatomic,retain) NSMutableDictionary *mDic;
@end
@implementation RootTableViewController
-(void)dealloc{
[_mDic release];
[_mArray release];
[super dealloc];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self configureNavigation];
[self readDataFromPlist];
#warning 使用自定义 cell, 需要进行注册(可以注册对个自定义cell)
[self.tableView registerClass:[BoyTableViewCell class] forCellReuseIdentifier:kBoyCell];
[self.tableView registerClass:[GirlTableViewCell class] forCellReuseIdentifier:kGirlCell];
}
// 导航条添加标题
- (void)configureNavigation{
self.navigationItem.title = @"通讯录";
}
//读取并存储文件
- (void)readDataFromPlist{
// 1.获取文件路径
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"StudentList" ofType:@"plist"];
// 2.获取文件中的数据存放到字典中
NSArray *array = [NSArray arrayWithContentsOfFile:filePath];
//通过打印的方法,检查是否读取了数据
NSLog(@"%@",array);
// 3.初始化定义的可变数组
self.mArray = [NSMutableArray arrayWithCapacity:0];
// 4.遍历存放数据的数组 array
for (NSDictionary *dic in array) {
// 5.创建学生对象
Student *stu = [[Student alloc]init];
// 6.将字典中的数据封装成 stu 对象(比较重要的方法)
[stu setValuesForKeysWithDictionary:dic];
// 7.将封装的对象添加到数组中
[self.mArray addObject:stu];
[stu release];
}
}
#pragma mark - Table view data source
// 返回分区的个数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// 放回每个分区的行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.mArray.count;
}
// 添加cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
#warning 使用自定义 cell ,获取数据
// 使用自定义数据创建 cell
// 1.取出数据
Student *stu = self.mArray[indexPath.row];
// 2.根据性别选择不同的 cell
if ([stu.gender isEqualToString:@"女"]) {
GirlTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGirlCell forIndexPath:indexPath];
cell.student = stu;
return cell;
}else{
BoyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kBoyCell forIndexPath:indexPath];
cell.student = stu;
return cell;
}
}
// 返回自定义 cell 的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 130;
}
#import "BoyTableViewCell.h"
#import "Student.h"
@implementation BoyTableViewCell
- (void)dealloc{
[_phoneLable release];
[_ageLable release];
[_images release];
[_nameLable release];
[_student release];
[super dealloc];
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self p_setUp];
}
return self;
}
- (void)p_setUp{
self.nameLable = [[UILabel alloc]initWithFrame:CGRectMake(10, 5, 100, 40)];
self.nameLable.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.nameLable];
[self.nameLable release];
self.ageLable = [[UILabel alloc]initWithFrame:CGRectMake(CGRectGetMaxX(self.nameLable.frame) + 20, CGRectGetMinY(self.nameLable.frame), CGRectGetWidth(self.nameLable.frame) - 20, CGRectGetHeight(self.nameLable.frame))];
self.ageLable.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.ageLable];
[self.ageLable release];
self.phoneLable = [[UILabel alloc]initWithFrame:CGRectMake(CGRectGetMinX(self.nameLable.frame),CGRectGetMaxY(self.nameLable.frame) + 40,CGRectGetWidth(self.nameLable.frame) * 2,CGRectGetHeight(self.nameLable.frame))];
self.phoneLable.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.phoneLable];
[self.phoneLable release];
self.images = [[UIImageView alloc]initWithFrame:CGRectMake(CGRectGetMaxX(self.ageLable.frame) + 20, CGRectGetMinY(self.nameLable.frame), CGRectGetWidth(self.ageLable.frame), CGRectGetHeight(self.nameLable.frame) * 3)];
self.images.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:self.images];
[self.images release];
}
// cell 使用 Model 展示数据,所以需要重写 setter 方法
- (void)setStudent:(Student *)student{
// 1.存数数据对象
if (_student != student) {
[_student release];
_student = [student retain];
}
// 2.将传入的数据赋值到 cell子视图上显示
self.nameLable.text = student.name;
self.ageLable.text = student.age;
self.phoneLable.text = student.phone;
self.images.image = [UIImage imageNamed:student.imageName];
}
@end
Student.h
#import
@interface Student : NSObject
// model 的属性要根据存储的数据类型合和个数来确定,属性名尽量拷贝
@property (nonatomic,retain) NSString *name;
@property (nonatomic,retain) NSString *gender;
@property (nonatomic,retain) NSString *age;
@property (nonatomic,retain) NSString *phone;
@property (nonatomic,retain) NSString *imageName;
@end
#import "Student.h"
@implementation Student
- (void)dealloc{
[_imageName release];
[_phone release];
[_gender release];
[_age release];
[_name release];
[super dealloc];
}
// 防止没有定义 key 值时,还在为对应的 key 的 value 赋值 崩溃的问题
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
@end
4.cell 文本自适应高度方法
// 重写 setter方法
- (void)setNews:(News *)news{
// 1.给属性赋值
if (_news != news) {
[_news release];
_news = [news retain];
}
// 2.使用 news 给 cell 上的子视图赋值
self.titleLable.text = news.title;
self.textLable.text = news.summary;
#warning 修改 textLable 的高度
// 1.先取出 frame 的值
CGRect textRect = self.textLable.frame;
// 2.修改 frame 的高,在对象方法中,使用类的时候使用[self class]
textRect.size.height = [[self class] summaryHeight:news.summary];
// 3,重新为 frame 赋值
self.textLable.frame = textRect;
}
#warning 计算文本自适应高度核心算法
+(CGFloat)cellHeigth:(News *)news{
CGFloat summaryHeight = [self summaryHeight:news.summary];
return 10 + 40 + 10 + 20 + summaryHeight;
}
// 计算文本的高度
+ (CGFloat)summaryHeight:(NSString *)summary{
//文本渲染时需要的矩形大小,按要求宽度要和文本大小的宽一样,必须是固定值(280),高度可以设置为(0或10000),最终文本高度大小是根据文本内容计算得到的
CGSize size = CGSizeMake(280, 0);
// 计算时设置的字体大小,必须要和我们显示文本设置字体大小保持一致
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]};
CGRect summaryRect = [summary boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
// 返回计算得到的高度即可
return summaryRect.size.height;
}
// 代理方法在试图控制器文件实现
// delegate 返回 cell 的高度,这个方法要比 tableView: cellForRowAtIndexPath:方法提前执行
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [NewsCell cellHeigth:self.mArray[indexPath.row]];
}