自定义UITableViewCell新浪微博

自定义UITableViewCell新浪微博

自定义 UITableViewCell的步骤:

1. 新建一个继承自 UITableViewCell 的类

2. 重写 initWithStyle:reuseIdentifier: 方法
添加所有需要显示的子控件 ( 不需要设置子控件的数据和 frame,   子控件要添加到 contentView )
进行子控件一次性的属性设置 ( 有些属性只需要设置一次 比如字体 \ 固定的图片 )

3. 提供 2 个模型
数据模型 存放文字数据 \ 图片数据
frame
模型 存放数据模型 \ 所有子控件的 frame\cell 的高度

4. cell 拥有一个 frame 模型 ( 不要直接拥有数据模型 )

5. 重写 frame 模型属性的 setter 方法 在这个方法中设置子控件的显示数据和 frame

6.f rame 模型数据的初始化已经采取懒加载的方式 ( 每一个 cell 对应的 frame 模型数据只加载一次 )

步骤一:根据 plist文件,创建Model层数据。

@property  ( nonatomic , copy NSString  *text; //  正文
@property  ( nonatomic , copy NSString  *icon; //  头像
@property  ( nonatomic , copy NSString  *picture; //  图片
@property  ( nonatomic , copy NSString  *name; // name
@property  ( nonatomic , assign BOOL  vip; // vip

+ (
instancetype )weiboWithDict:( NSDictionary  *)dict;
- (
instancetype )initWithDict:( NSDictionary  *)dict;
+ ( NSArray *) weibos ;

- ( instancetype )initWithDict:( NSDictionary *)dict
{
   
if ( self = [ super init ]) {
        [
self setValuesForKeysWithDictionary :dict];
    }
   
return self ;
}

+ (
instancetype )weiboWithDict:( NSDictionary *)dict
{
   
return [[ self alloc ] initWithDict :dict];
}

+ ( NSArray *)weibos
{
   
NSArray *array = [ NSArray arrayWithContentsOfFile :[[ NSBundle mainBundle ] pathForResource : @"statuses.plist" ofType : nil ]];
   
   
NSMutableArray *arrayM = [ NSMutableArray array ];
   
for ( NSDictionary *dict in array) {
        [arrayM
addObject :[ self statusWithDict :dict]];
    }
   
return arrayM;
}

步骤二:实现数据源方法,查看数据是否能显示出来。
- ( NSArray *)weibos
{
   
if ( _weibos == nil ) {
       
_weibos = [ SUNWeiboItem   weibos ];
    }
   
return _weibos ;
}

#pragma mark - 数据源方法
- ( NSInteger )tableView:( UITableView *)tableView numberOfRowsInSection:( NSInteger )section
{
   
return self . statuses . count ;
}

- (
UITableViewCell *)tableView:( UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath
{
   
static NSString *ID = @"cell" ;
   
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier :ID];
   
if (cell == nil ) {
        cell = [[
UITableViewCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier :ID];
    }
   
SUNWeiboItem *weiboItem = self . statuses [indexPath. row ];
    cell.
textLabel . text = weiboItem. name ;
   
return cell;
}


步骤三:创建自定义 SUNWeiboCell 继承自 UITableViewCell, 根据需求,确定控件,并定义属性。
@property ( nonatomic , strong ) UIImageView *iconView;
@property ( nonatomic , strong ) UILabel *nameView;
@property ( nonatomic , strong ) UIImageView *vipView;
@property ( nonatomic , strong ) UILabel *textView;
@property ( nonatomic , strong ) UIImageView *pictureView;

步骤四、 getter 方法完成控件的实例化,只创建并添加到 contentView ,不处理位置。
/** 姓名字体 */
#define kNameFont   [UIFont systemFontOfSize: 14 ]
/** 正文字体 */
#define kTextFont   [UIFont systemFontOfSize: 16 ]
- ( UIImageView *)iconView
{
   
if ( _iconView == nil ) {
       
_iconView = [[ UIImageView alloc ] init ];
        [
self . contentView addSubview : _iconView ];
    }
   
return _iconView ;
}

- (
UILabel *)nameView
{
   
if ( _nameView == nil ) {
       
_nameView = [[ UILabel alloc ] init ];
       
// 默认字体是 17
       
_nameView . font = kNameFont ;
        [
self . contentView addSubview : _nameView ];
    }
   
return _nameView ;
}
注意:
UILabel中默认的字体是17号。

- (
UIImageView *)vipView
{
   
if ( _vipView == nil ) {
       
_vipView = [[ UIImageView alloc ] init ];
       
_vipView . image = [ UIImage imageNamed : @"vip" ];
       
_vipView . hidden = YES ;
        [
self . contentView addSubview : _vipView ];
    }
   
return _vipView ;
}

- (
UILabel *)textView
{
   
if ( _textView == nil ) {
       
_textView = [[ UILabel alloc ] init ];
       
_textView . font = kTextFont ;
       
_textView . numberOfLines = 0 ;
        [
self . contentView addSubview : _textView ];
    }
   
return _textView ;
}
注意:
numberOfLines属性是换行的意思。
- (
UIImageView *)pictureView
{
   
if ( _pictureView == nil ) {
       
_pictureView = [[ UIImageView alloc ] init ];
        [
self . contentView addSubview : _pictureView ];
    }
   
return _pictureView ;
}

步骤五:在自定义cell里面, 定义一个模型属性,通过 setter 方法,设置 cell 的显示。
@property ( nonatomic , strong ) SUNWeiboItem *weiboItem;
- ( void )setWeiboItem:( SUNWeiboItem *)weiboItem
{
   
_weiboItem = weiboItem;
   
// 1> 设置数据
    [
self settingData ];
   
   
// 2> 设置位置
    [
self settingFrame ];
}

注意:
在主方法中,代码不宜过长。
/** 设置数据 */
- (
void )settingData
{
   
// 头像
   
self . iconView . image = [ UIImage imageNamed : self . weiboItem . icon ];
   
// 姓名
   
self . nameView . text = self . weiboItem . name ;
   
// vip( 可选的 )
   
if ( self . weiboItem . vip ) {
       
self . vipView . hidden = NO ;
       
self . nameView . textColor = [ UIColor redColor ];
    }
else {
       
self . vipView . hidden = YES ;
       
self . nameView . textColor = [ UIColor blackColor ];
    }
   
   
// 正文
   
self . textView . text = self . weiboItem . text ;
   
   
// 配图 ( 可选参数 )
   
if ( self . weiboItem . picture . length > 0 ) {
       
self . pictureView . hidden = NO ;
       
self . pictureView . image = [ UIImage imageNamed : self . weiboItem . picture ];
    }
else {
       
self . pictureView . hidden = YES ;
    }
}
/** 设置位置 */
- (
void )settingFrame
{
   
// 0. 定义间距
   
CGFloat padding = 10 ;
   
   
// 1. 头像
   
CGFloat iconX = padding;
   
CGFloat iconY = padding;
   
CGFloat iconW = 30 ;
   
CGFloat iconH = 30 ;
   
self . iconView . frame = CGRectMake (iconX, iconY, iconW, iconH);
   
   
// 2. 姓名大小由文字的长度来决定
   
NSDictionary *nameDict = @{ NSFontAttributeName : kNameFont } ;
   
CGRect nameFrame = [ weiboItem . name boundingRectWithSize : CGSizeMake ( MAXFLOAT , MAXFLOAT ) options : NSStringDrawingUsesLineFragmentOrigin attributes :nameDict context : nil ];
    nameFrame.
origin . x = CGRectGetMaxX ( self . iconView . frame ) + padding;
    nameFrame.
origin . y = padding + ( self . iconView . bounds . size . height - nameFrame. size . height ) * 0.5 ;
   
self . nameView . frame = nameFrame;
    注意:
    文字的大小,最好和vip图标的宽度一致,这样比较容易计算出vip的位置。
   
// vip 图标
   
CGFloat vipX = CGRectGetMaxX ( self . nameView . frame ) + padding;
   
CGFloat vipY = self . nameView . frame . origin . y ;
   
CGFloat vipW = 14 ;
   
CGFloat vipH = 14 ;
   
self . vipView . frame = CGRectMake (vipX, vipY, vipW, vipH);
   
   
// 正文
   
NSDictionary *textDict = @{ NSFontAttributeName : kTextFont } ;
   
CGRect textFrame = [ weiboItem . text boundingRectWithSize : CGSizeMake ( 300 , MAXFLOAT ) options : NSStringDrawingUsesLineFragmentOrigin attributes :textDict context : nil ];
    textFrame.
origin . x = padding;
    textFrame.
origin . y = CGRectGetMaxY ( self . iconView . frame ) + padding;
   
self . textView . frame = textFrame;
    注意:
    boundingRectWithSize属性,如果文字不换行的话,可以设置为CGSizeMake(MAXFLOATMAXFLOAT)。如果文字需要换行的话,需要指定宽度,即CGSizeMake(300MAXFLOAT)。
   
CGFloat cellHeight;
   
   
if ( weiboItem . picture . length > 0 ) {
       
// 配图
       
CGFloat pictureX = padding;
       
CGFloat pictureY = CGRectGetMaxY (textFrame) + padding;
       
CGFloat pictureW = 100 ;
       
CGFloat pictureH = 100 ;
       
self . pictureView . frame = CGRectMake (pictureX, pictureY, pictureW, pictureH);

        注意:
        一般在自定义表格中的配图的宽度和高度是固定死的,这样容易计算配图的位置。        
        cellHeight = CGRectGetMaxY ( self . pictureView . frame ) + padding;
    }
else {
        cellHeight =
CGRectGetMaxY ( self . textView . frame ) + padding;
    }
}

注意:
1.boundingRectWithSize计算给定文本字符串所占的区域返回值是一个x,y = 0CGRect,w,h是计算好的宽高。
2.如果要计算多行的准确高度,需要传入NSStringDrawingUsesLineFragmentOrigin选项。
3.attributes属性用于指定字体的相关属性的字典,UIKit框架中的第一个头文件。
4.context: nil

步骤六、在控制器实现 UITableView的代理方法调整为
#pragma mark -  数据源方法
- ( NSInteger )tableView:( UITableView  *)tableView numberOfRowsInSection:( NSInteger )section
{
    
return   self . statuses . count ;
}
- (
UITableViewCell  *)tableView:( UITableView  *)tableView cellForRowAtIndexPath:( NSIndexPath  *)indexPath
{
    NSLog ( @“222222222222222222" );
    
static   NSString  *ID =  @"cell" ;
    
SUNWeiboCell  *cell = [tableView  dequeueReusableCellWithIdentifier :ID];
    
if  (cell ==  nil ) {
        cell = [[
SUNWeiboCell   alloc initWithStyle : UITableViewCellStyleDefault   reuseIdentifier :ID];
    }
    cell.
weiboItem  =  self . statuses [indexPath. row ];
    
return  cell;
}

/** 计算单元格行高 */
- (
CGFloat )tableView:( UITableView *)tableView heightForRowAtIndexPath:( NSIndexPath *)indexPath
{

   
NSLog ( @"111111111111111111111" );
   
return  200 ;
}


问题分析:
1.在加载表格数据调用此方法前- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath,会首先调用- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath方法计算所有单元格的高度,而此时单元格尚未被实例化。
2.因此获取每一行的高度,不能直接从cell对象中获取,因为那时候还没创建cell对象,因此我们需要另外创建一个模型,用来管理cell内部所有子控件的framecell的总高度。

解决思路:既然"单元格的行高是通过模型的实际内容计算的。
那么,在加载数据模型时,应该就可以准确地计算出每一行的准确行高了。

解决办法:创建
SUNWeiboFrameItem  模型,专门处理行高的计算。

步骤七、 创建 SUNWeiboFrameItem 模型,专门处理行高的计算。
/** 专门计算所有控件位置 */
@interface SUNWeiboFrameItem : NSObject

@property ( nonatomic , assign ) CGRect iconImageViewF; // 头像 View
@property ( nonatomic , assign ) CGRect titleLabelF; // nameView
@property ( nonatomic , assign ) CGRect pictureImageF; // 图片 View
@property ( nonatomic , assign ) CGRect zwLabelF; // 正文 Label
@property ( nonatomic , assign ) CGRect vipImageViewF; // vip

/** 行高 */
@property ( nonatomic , assign ) CGFloat cellHeight;
/** 所有控件的尺寸都可以通过 Status 来计算得出 */
@property ( nonatomic , strong ) SUNWeiboItem *weiboItem;

+ (
NSArray *)weiboFrames;
@end
注意:创建 SUNWeiboFrameItem模型,通setter方法计算行高。
- ( void )setWeiboItem:( SUNWeiboItem *)weiboItem
{
   
_weiboItem = weiboItem;
   
// 0. 定义间距
   
CGFloat padding = 10 ;
   
   
// 1. 头像
   
CGFloat iconX = padding;
   
CGFloat iconY = padding;
   
CGFloat iconW = 30 ;
   
CGFloat iconH = 30 ;
   
self . iconImageViewF = CGRectMake (iconX, iconY, iconW, iconH);
   
   
// 2. 姓名大小由文字的长度来决定
   
NSDictionary *nameDict = @{ NSFontAttributeName : kNameFont } ;
   
CGRect nameFrame = [ self . weiboItem . name boundingRectWithSize : CGSizeMake ( MAXFLOAT , MAXFLOAT ) options : NSStringDrawingUsesLineFragmentOrigin attributes :nameDict context : nil ];
    nameFrame.
origin . x = CGRectGetMaxX ( self . iconImageViewF ) + padding;
    nameFrame.
origin . y = padding + ( self . iconImageViewF . size . height - nameFrame. size . height ) * 0.5 ;
   
self . titleLabelF = nameFrame;
   
   
// vip 图标
   
CGFloat vipX = CGRectGetMaxX ( self . titleLabelF ) + padding;
   
CGFloat vipY = self . titleLabelF . origin . y ;
   
CGFloat vipW = 14 ;
   
CGFloat vipH = 14 ;
   
self . vipImageViewF = CGRectMake (vipX, vipY, vipW, vipH);
   
   
// 正文
   
NSDictionary *textDict = @{ NSFontAttributeName : kTextFont } ;
   
CGRect textFrame = [ self . weiboItem . text boundingRectWithSize : CGSizeMake ( 300 , MAXFLOAT ) options : NSStringDrawingUsesLineFragmentOrigin attributes :textDict context : nil ];
    textFrame.
origin . x = padding;
    textFrame.
origin . y = CGRectGetMaxY ( self . iconImageViewF ) + padding;
   
self . zwLabelF = textFrame;
   
   
if ( self . weiboItem . picture . length > 0 ) {
       
// 配图
       
CGFloat pictureX = padding;
       
CGFloat pictureY = CGRectGetMaxY (textFrame) + padding;
       
CGFloat pictureW = 100 ;
       
CGFloat pictureH = 100 ;
       
self . pictureImageF = CGRectMake (pictureX, pictureY, pictureW, pictureH);
       
       
self . cellHeight = CGRectGetMaxY ( self . pictureImageF ) + padding;
    }
else {
       
self . cellHeight = CGRectGetMaxY ( self . zwLabelF ) + padding;
    }
}

+ ( NSArray *)weiboFrames
{
   
NSString *filePath = [[ NSBundle mainBundle ] pathForResource : @"statuses.plist" ofType : nil ];
   
NSArray *array = [ NSArray arrayWithContentsOfFile :filePath];
   
   
NSMutableArray *arrayM = [ NSMutableArray array ];
   
for ( NSDictionary *dict in array) {
       
SUNWeiboFrameItem *weiboFrameItem = [[ SUNWeiboFrameItem alloc ] init ];
        weiboFrameItem.
weiboItem = [ SUNWeiboItem weiboWithDict :dict];
        [arrayM
addObject :weiboFrameItem];
    }
   
return arrayM;
}


注意:
所有的单元格控件的计算都是重复的,而且每次表格滚动,设置内容都会重新计算。
解决这个问题:
SUNWeiboFrameItem模型中,已经包含了 weiboItem属性。
目前控制器中的数组保存的是 weiboItem模型,将 weiboItem模型替换为SUNWeiboFrameItem模型{weiboItem,所有控件的位置},同样具有weiboItem模型。

步骤八、重构调整 SUNWeiboItem SUNWeiboFrameItem
SUNWeiboItem模型中删除 weibos数组。
SUNWeiboFrameItem添加 weiboFrames数组。

步骤九、重构控制器 实现 UITableView的代理方法调整为
- ( NSArray *)weiboFrames
{
   
if ( _weiboFrames == nil ) {
       
_weiboFrames = [ SUNWeiboFrameItem weiboFrames ];
    }
   
return _weiboFrames ;
}

注意:
weiboFrames数组要改变成SUNWeiboFrameItem的模型数据。
#pragma mark - 数据源方法
- ( NSInteger )tableView:( UITableView *)tableView numberOfRowsInSection:( NSInteger )section
{
   
return self . weiboFrames . count ;
}

- (
UITableViewCell *)tableView:( UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath
{
   
static NSString *ID = @"cell" ;
   
SUNWeiboCell *cell = [tableView dequeueReusableCellWithIdentifier :ID];
   
if (cell == nil ) {
        cell = [[
SUNWeiboCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier :ID];
    }
   
SUNWeiboFrameItem *weiboFrameItem = self . weiboFrames [indexPath. row ];
    cell.
weiboItem = weiboFrameItem. weiboItem ;
   
return cell;
}

/** 计算单元格行高 */
- (
CGFloat )tableView:( UITableView *)tableView heightForRowAtIndexPath:( NSIndexPath *)indexPath
{
   
NSLog ( @"111111111111111111111" );
   
SUNWeiboFrameItem *weiboFrameItem = self . weiboFrames [indexPath. row ];
   
return weiboFrameItem. cellHeight ;
}

问题分析:
cell中的控件的位置计算,依然是自己计算的。
解决办法:将cell中的status替换为statusFrame就可以拿到控件位置。


步骤十、重构 自定义 SUNWeiboCell,将 SUNWeiboItem模型数据替换为 SUNWeiboFrameItem模型数据。
@property ( nonatomic , strong ) SUNWeiboItem *weiboItem;

- ( void )setWeiboFrameItem:( SUNWeiboFrameItem *)weiboFrameItem
{
   
_weiboFrameItem = weiboFrameItem;
   
// 1> 设置数据
    [
self settingData ];
   
   
// 2> 设置位置
    [
self settingFrame ];
}


/** 设置数据 */
- (
void )settingData
{
   
SUNWeiboItem *weiboItem = self . weiboFrameItem . weiboItem ;
   
// 头像
   
self . iconView . image = [ UIImage imageNamed :weiboItem. icon ];
   
// 姓名
   
self . nameView . text = weiboItem. name ;
   
// vip( 可选的 )
   
if (weiboItem. vip ) {
       
self . vipView . hidden = NO ;
       
self . nameView . textColor = [ UIColor redColor ];
    }
else {
       
self . vipView . hidden = YES ;
       
self . nameView . textColor = [ UIColor blackColor ];
    }
   
   
// 正文
   
self . textView . text = weiboItem. text ;
   
   
// 配图 ( 可选参数 )
   
// imageNamed:nil CUICatalog: Invalid asset name supplied: (null), or invalid scale factor: 2.000000
   
if (weiboItem. picture . length > 0 ) {
       
self . pictureView . hidden = NO ;
       
self . pictureView . image = [ UIImage imageNamed :weiboItem. picture ];
    }
else {
       
self . pictureView . hidden = YES ;
    }
}

/** 设置位置 */
- (
void )settingFrame
{
   
self . iconView . frame = self . weiboFrameItem . iconImageViewF ;
   
   
// 2. 姓名大小由文字的长度来决定
   
self . nameView . frame = self . weiboFrameItem . titleLabelF ;
   
   
// vip 图标
   
self . vipView . frame = self . weiboFrameItem . vipImageViewF ;
   
   
// 正文
   
self . textView . frame = self . weiboFrameItem . zwLabelF ;
   
   
if ( self . weiboFrameItem . weiboItem . picture . length > 0 ) {
       
// 配图
       
self . pictureView . frame = self . weiboFrameItem . pictureImageF ;
    }
}


步骤十一、 重构控制器 实现 UITableView的代理方法调整为
- ( UITableViewCell *)tableView:( UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath
{
   
static NSString *ID = @"cell" ;
   
SUNWeiboCell *cell = [tableView dequeueReusableCellWithIdentifier :ID];
   
if (cell == nil ) {
        cell = [[
SUNWeiboCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier :ID];
    }
    cell.
weiboFrameItem = self . weiboFrames [indexPath. row ];
   
return cell;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值