iOS开发UI篇—以微博界面为例使用纯代码自定义cell程序编码全过程(二)
iOS开发UI篇—以微博界面为例使用纯代码自定义cell程序编码全过程(二)
一、需要改进的地方
还需改进的地方:cell的高度需要根据每条微博的数据进行动态设置。
设置cell的高度可以有两种方式,一种是通过rowheight属性来进行设置,一种是通过代理来进行设置。通过属性设置适用于每行的高度一致,使用代理适用于每行的高度不一致的情况。
二、实现思路
在这个应用中,每个cell的高度是根据内容来确定的,所以在这里我们通过代理来设置cell的高度。
获取到图片最大的Y值或者是文字最大的Y值,在cell中设置一个新的变量。
判断一下,如果有配图,则高度等于配图的高度+间隙的高度,如果没有配图那么高度等于文字的高度+间隙的高度。
在自定义cell的setting frame方法中计算出行高,而要在主控制器中进行使用,怎么办才能拿到数据?
计算行高的方法最终是由cellforrow(setweibo->settingframe->计算行高)调用的,如果要拿到行高的话,需要先调用cellforrow这个方法。
查看cellforrow和heughtforrow这两个方法是谁先调用,结果显示是heiforrow先调用。
拿不到计算的行高?怎么办?
让它在计算heightforrow之前就计算出行高。计算行高,依赖于其他控件中的位置,位置依赖于模型中的数据。
拿到模型数据,就可以计算出高度。
那么在哪里可以拿到数据模型呢?
在懒加载中创建模型,就可以拿到模型,计算所有控件的frame,在懒加载中就可以获得行高。
拿到模型后可以计算出5个frame,那么就使用一个模型,把着5个frame保存起来,将来一个自定义的cell就对应一个frame模型。
新建一个类,继承自nsobject,这个类专门用来保存每一行数据的frame。在类中创建5个对应的frame属性,(CGRECT)以及一个行高。添加一个模型数据,当别人给我一个模型数据的时候,我就可以通过重写set方法,设置模型数据的frame.
把之前的frame计算方法拷贝过去。(为什么?)
在懒加载方法中,根据模型数据创建frame模型,往数组里面添加的时候添加frame模型,此时该数组中既有所有的数据模型,又拥有对应的frame。
在代理方法中,获取到当前索引对应的frame。
三、实现代码
1.项目文件结构
2.代码
模型部分
TXStatus.h文件
1 // Created by 鑫 on 14-10-12. 2 // Copyright (c) 2014年 梁镋鑫. All rights reserved. 3 // 4 5 #import <Foundation/Foundation.h> 6 7 @interface TXStatus : NSObject 8 @property (nonatomic, copy) NSString *text; // 内容 9 @property (nonatomic, copy) NSString *icon; // 头像 10 @property (nonatomic, copy) NSString *name; // 昵称 11 @property (nonatomic, copy) NSString *picture; // 配图 12 @property (nonatomic, assign) BOOL vip; 13 14 - (instancetype)initWithDict:(NSDictionary *)dict; 15 + (instancetype)statusWithDict:(NSDictionary *)dict; 16 17 @end
TXStatus.m文件
1 #import "TXStatus.h" 2 #import "TXStatus.h" 3 @implementation TXStatus 4 - (instancetype)initWithDict:(NSDictionary *)dict 5 { 6 if (self = [super init]) { 7 [self setValuesForKeysWithDictionary:dict]; 8 } 9 return self; 10 } 11 12 + (instancetype)statusWithDict:(NSDictionary *)dict 13 { 14 return [[self alloc] initWithDict:dict]; 15 } 16 17 @end
视图部分
TXStatusCell.h文件
1 #import <UIKit/UIKit.h> 2 @class TXStatusFrame; 3 @interface TXStatusCell : UITableViewCell 4 @property (nonatomic, strong) TXStatusFrame *statusFrame; 5 + (instancetype)cellWithTableView:(UITableView *)tableView; 6 @end
TXStatusCell.m文件
1 // 2 // 昵称的字体 3 #define TXNameFont [UIFont systemFontOfSize:14] 4 // 正文的字体 5 #define TXTextFont [UIFont systemFontOfSize:15] 6 #import "TXStatusCell.h" 7 #import "TXStatus.h" 8 #import "TXStatusFrame.h" 9 @interface TXStatusCell() 10 11 /** 12 * 头像 13 */ 14 @property (nonatomic, weak) UIImageView *iconView; 15 /** 16 * 昵称 17 */ 18 @property (nonatomic, weak) UILabel *nameView; 19 /** 20 * 会员图标 21 */ 22 @property (nonatomic, weak) UIImageView *vipView; 23 /** 24 * 正文 25 */ 26 @property (nonatomic, weak) UILabel *textView; 27 /** 28 * 配图 29 */ 30 @property (nonatomic, weak) UIImageView *pictureView; 31 32 33 @end 34 @implementation TXStatusCell 35 /** 36 * 构造方法(在初始化对象的时候会调用) 37 * 一般在这个方法中添加需要显示的子控件 38 */ 39 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 40 { 41 self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 42 if (self) { 43 // 1.头像 44 UIImageView *iconView = [[UIImageView alloc] init]; 45 [self.contentView addSubview:iconView]; 46 self.iconView = iconView; 47 48 // 2.昵称 49 UILabel *nameView = [[UILabel alloc] init]; 50 // nameView.backgroundColor = [UIColor redColor]; 51 nameView.font = TXNameFont; 52 [self.contentView addSubview:nameView]; 53 self.nameView = nameView; 54 55 // 3.会员图标 56 UIImageView *vipView = [[UIImageView alloc] init]; 57 vipView.image = [UIImage imageNamed:@"vip"]; 58 [self.contentView addSubview:vipView]; 59 self.vipView = vipView; 60 61 // 4.正文 62 UILabel *textView = [[UILabel alloc] init]; 63 // textView.backgroundColor = [UIColor blueColor]; 64 textView.numberOfLines = 0; 65 textView.font = TXTextFont; 66 [self.contentView addSubview:textView]; 67 self.textView = textView; 68 69 // 5.配图 70 UIImageView *pictureView = [[UIImageView alloc] init]; 71 [self.contentView addSubview:pictureView]; 72 self.pictureView = pictureView; 73 } 74 return self; 75 } 76 /** 77 * 在这个方法中设置子控件的frame和显示数据 78 */ 79 - (void)setStatusFrame:(TXStatusFrame *)statusFrame 80 { 81 _statusFrame = statusFrame; 82 83 // 1.设置数据 84 [self settingData]; 85 86 // 2.设置frame 87 [self settingFrame]; 88 } 89 /** 90 * 设置数据 91 */ 92 - (void)settingData 93 { 94 // 微博数据 95 TXStatus *status = self.statusFrame.status; 96 97 // 1.头像 98 self.iconView.image = [UIImage imageNamed:status.icon]; 99 100 // 2.昵称 101 self.nameView.text = status.name; 102 103 // 3.会员图标 104 if (status.vip) { 105 self.vipView.hidden = NO; 106 107 self.nameView.textColor = [UIColor redColor]; 108 } else { 109 self.vipView.hidden = YES; 110 111 self.nameView.textColor = [UIColor blackColor]; 112 } 113 114 // 4.正文 115 self.textView.text = status.text; 116 117 // 5.配图 118 if (status.picture) { // 有配图 119 self.pictureView.hidden = NO; 120 self.pictureView.image = [UIImage imageNamed:status.picture]; 121 } else { // 没有配图 122 self.pictureView.hidden = YES; 123 } 124 } 125 126 /** 127 * 计算文字尺寸 128 * 129 * @param text 需要计算尺寸的文字 130 * @param font 文字的字体 131 * @param maxSize 文字的最大尺寸 132 */ 133 - (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font maxSize:(CGSize)maxSize 134 { 135 NSDictionary *attrs = @{NSFontAttributeName : font}; 136 return [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size; 137 } 138 139 /** 140 * 设置frame 141 */ 142 /** 143 * 设置frame 144 */ 145 - (void)settingFrame 146 { 147 // 1.头像 148 self.iconView.frame = self.statusFrame.iconF; 149 150 // 2.昵称 151 self.nameView.frame = self.statusFrame.nameF; 152 153 // 3.会员图标 154 self.vipView.frame = self.statusFrame.vipF; 155 156 // 4.正文 157 self.textView.frame = self.statusFrame.textF; 158 159 // 5.配图 160 if (self.statusFrame.status.picture) {// 有配图 161 self.pictureView.frame = self.statusFrame.pictureF; 162 } 163 } 164 + (instancetype)cellWithTableView:(UITableView *)tableView 165 { 166 167 static NSString *ID = @"status"; 168 TXStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; 169 if (cell == nil) { 170 cell = [[TXStatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; 171 } 172 return cell; 173 174 } 175 176 - (void)awakeFromNib 177 { 178 // Initialization code 179 } 180 181 - (void)setSelected:(BOOL)selected animated:(BOOL)animated 182 { 183 [super setSelected:selected animated:animated]; 184 185 // Configure the view for the selected state 186 } 187 188 @end
TXStatusFrame.h文件
1 // Created by 鑫 on 14-10-12. 2 // Copyright (c) 2014年 梁镋鑫. All rights reserved. 3 // 4 5 #import <Foundation/Foundation.h> 6 @class TXStatus; 7 @interface TXStatusFrame : NSObject 8 /** 9 * 头像的frame 10 */ 11 @property (nonatomic, assign, readonly) CGRect iconF; 12 /** 13 * 昵称的frame 14 */ 15 @property (nonatomic, assign, readonly) CGRect nameF; 16 /** 17 * 会员图标的frame 18 */ 19 @property (nonatomic, assign, readonly) CGRect vipF; 20 /** 21 * 正文的frame 22 */ 23 @property (nonatomic, assign, readonly) CGRect textF; 24 /** 25 * 配图的frame 26 */ 27 @property (nonatomic, assign, readonly) CGRect pictureF; 28 29 /** 30 * cell的高度 31 */ 32 @property (nonatomic, assign, readonly) CGFloat cellHeight; 33 @property (nonatomic, strong) TXStatus *status; 34 @end
TXStatusFrame.m文件
1 // Created by 鑫 on 14-10-12. 2 // Copyright (c) 2014年 梁镋鑫. All rights reserved. 3 // 4 // 昵称的字体 5 #define TXNameFont [UIFont systemFontOfSize:14] 6 // 正文的字体 7 #define TXTextFont [UIFont systemFontOfSize:15] 8 9 #import "TXStatusFrame.h" 10 #import "TXStatus.h" 11 @implementation TXStatusFrame 12 /** 13 * 计算文字尺寸 14 * 15 * @param text 需要计算尺寸的文字 16 * @param font 文字的字体 17 * @param maxSize 文字的最大尺寸 18 */ 19 - (CGSize)sizeWithText:(NSString *)text font:(UIFont *)font maxSize:(CGSize)maxSize 20 { 21 NSDictionary *attrs = @{NSFontAttributeName : font}; 22 return [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size; 23 } 24 -(void)setStatus:(TXStatus *)status 25 { _status = status; 26 27 // 子控件之间的间距 28 CGFloat padding = 10; 29 30 // 1.头像 31 CGFloat iconX = padding; 32 CGFloat iconY = padding; 33 CGFloat iconW = 30; 34 CGFloat iconH = 30; 35 _iconF = CGRectMake(iconX, iconY, iconW, iconH); 36 37 // 2.昵称 38 // 文字的字体 39 CGSize nameSize = [self sizeWithText:self.status.name font:TXNameFont maxSize:CGSizeMake(MAXFLOAT, MAXFLOAT)]; 40 CGFloat nameX = CGRectGetMaxX(_iconF) + padding; 41 CGFloat nameY = iconY + (iconH - nameSize.height) * 0.5; 42 _nameF = CGRectMake(nameX, nameY, nameSize.width, nameSize.height); 43 44 // 3.会员图标 45 CGFloat vipX = CGRectGetMaxX(_nameF) + padding; 46 CGFloat vipY = nameY; 47 CGFloat vipW = 14; 48 CGFloat vipH = 14; 49 _vipF = CGRectMake(vipX, vipY, vipW, vipH); 50 51 // 4.正文 52 CGFloat textX = iconX; 53 CGFloat textY = CGRectGetMaxY(_iconF) + padding; 54 CGSize textSize = [self sizeWithText:self.status.text font:TXTextFont maxSize:CGSizeMake(300, MAXFLOAT)]; 55 _textF = CGRectMake(textX, textY, textSize.width, textSize.height); 56 57 // 5.配图 58 if (self.status.picture) {// 有配图 59 CGFloat pictureX = textX; 60 CGFloat pictureY = CGRectGetMaxY(_textF) + padding; 61 CGFloat pictureW = 100; 62 CGFloat pictureH = 100; 63 _pictureF = CGRectMake(pictureX, pictureY, pictureW, pictureH); 64 65 _cellHeight = CGRectGetMaxY(_pictureF) + padding; 66 } else { 67 _cellHeight = CGRectGetMaxY(_textF) + padding; 68 } 69 70 71 } 72 @end
控制器部分
TXViewController.h文件
1 #import <UIKit/UIKit.h> 2 3 @interface TXViewController : UITableViewController 4 5 @end
TXViewController.m文件
1 // Created by 鑫 on 14-10-12. 2 // Copyright (c) 2014年 梁镋鑫. All rights reserved. 3 // 4 5 #import "TXViewController.h" 6 #import "TXStatus.h" 7 #import "TXStatusCell.h" 8 #import "TXStatusFrame.h" 9 @interface TXViewController () 10 @property (nonatomic, strong) NSArray *statusFrames; 11 12 @end 13 14 @implementation TXViewController 15 - (void)viewDidLoad 16 { 17 [super viewDidLoad]; 18 // Do any additional setup after loading the view, typically from a nib. 19 //行高 20 self.tableView.rowHeight = 400; 21 } 22 23 - (NSArray *)statusFrames 24 { 25 if (_statusFrames == nil) { 26 // 初始化 27 // 1.获得plist的全路径 28 NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil]; 29 30 // 2.加载数组 31 NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; 32 33 // 3.将dictArray里面的所有字典转成模型对象,放到新的数组中 34 NSMutableArray *statusFrameArray = [NSMutableArray array]; 35 for (NSDictionary *dict in dictArray) { 36 // 3.1.创建TXStatus模型对象 37 TXStatus *status = [TXStatus statusWithDict:dict]; 38 39 // 3.2.创建TXStatusFrame模型对象 40 TXStatusFrame *statusFrame = [[TXStatusFrame alloc] init]; 41 statusFrame.status = status; 42 43 // 3.2.添加模型对象到数组中 44 [statusFrameArray addObject:statusFrame]; 45 } 46 47 // 4.赋值 48 _statusFrames = statusFrameArray; 49 } 50 return _statusFrames; 51 } 52 #pragma mark - 实现数据源方法 53 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 54 { 55 return self.statusFrames.count; 56 } 57 58 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 59 { 60 // 1.创建cell 61 TXStatusCell *cell = [TXStatusCell cellWithTableView:tableView]; 62 63 // 2.在这个方法算好了cell的高度 64 cell.statusFrame = self.statusFrames[indexPath.row]; 65 66 // 3.返回cell 67 return cell; 68 } 69 #pragma mark - 实现代理方法 70 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 71 { 72 73 // 取出这行对应的frame模型 74 TXStatusFrame *statusFrame = self.statusFrames[indexPath.row]; 75 return statusFrame.cellHeight;} 76 77 78 - (BOOL)prefersStatusBarHidden 79 { 80 return YES; 81 } 82 83 84 85 @end
3.实现效果
四、优化
在给自定义cell中重写set方法时,设置了微博的数据,还同时设置了frame,那么既然已经在frame模型中计算出了frame,这里就不需要再进行一次多余的计算了。修改代码,在cell里拿到weiboframe模型,就能拿到所有的frame。在自定义的cell里边,不再保存weibo而是保存weiboframe的属性。
说明:只对项目的三个文件进行了修改。
示意图: