前言:
自适应在日常工作中的场景还是相当广泛的,而当接收旧项目,看着满目疮痍的代码像一个残喘的病人,该如何下手,治疗病症;便是doctor该思考的问题了.如果是新页面的自适应,那还好说些,可是如果业务逻辑向关联性很多,一个界面被复用过n多次,牵一发动全身的情况就要好好斟酌了.
话不多说,不管iOS7,8还是9,10的自适应,最根本的应该就是frame的计算上.
虽然略显麻烦,但是确保可扩展可维护性这才是正确的.
场景介绍:
在tableview的cell中,有着控件个数不固定,长宽高不固定的情况;
比如,一个界面被复用了很多次,第一次进来,要求只有一个label,
但是label上的内容长度,高度都不固定,并且cell要对其进行自适应;
一个label的自适应,搞定了,那么就不会介意有imageview,等等的自定义的view了;所以接下来就详细的说一下cell自适应label的不固定的高和宽吧.
进入高能:
一个自定义cell,一个进行Frame计算的工具Frame类,如此便可.
在对应的控制中需要有个数组,用来保存loadData中得到的模型数据.
@property (nonatomic ,strong)NSArray *contentFrame;
最好在loadData中直接返回的是模型的数组,这样做在整体架构来讲是很好的处理方式
接着跟进模型数组创建frame模型数组,也就是对得到的原始数据进行加工,让它具有一个frame模型;
- (NSArray *)contentFrame
{
NSMutableArray *models = [NSMutableArray arrayWithCapacity:_dataList.count];
for (TCWorkMessageSamllList *lis in _dataList) {
// 根据模型数据创建frame模型
VDFrame *vdF = [[VDFrame alloc] init];
vdF.model = lis;
[models addObject:vdF];
}
self.contentFrame = [models copy];
return _contentFrame;
}
那来看一下VDFrame里面具体的实现吧
#import
#import "TCWorkMessageSamllList.h"
@interface VDFrame : NSObject
@property (nonatomic, strong) TCWorkMessageSamllList *model;
@property (nonatomic, assign) CGRect introF;
@property (nonatomic, assign) CGRect sLabelF;
@property (nonatomic, assign) CGRect tLabelF;
@property (nonatomic, assign) CGFloat cellHeight;
@end
- TCWorkMessageSamllList是一个模型,VDFrame需要跟进模型中的content来计算cell实际的高度;
// 专门用来保存每一行数据的frame, 计算frame
#import "VDFrame.h"
#define VDNameFont [UIFont systemFontOfSize:15]
#define VDTextFont [UIFont systemFontOfSize:16]
@implementation VDFrame
- (void)setModel:(TCWorkMessageSamllList *)model
{
_model = model;
// 间隙
CGFloat padding = 10;
CGFloat introLabelX = padding;
CGFloat introLabelY = padding;
CGSize textSize = [self sizeWithString:_model.content font:VDTextFont maxSize:CGSizeMake(300, MAXFLOAT)];
CGFloat introLabelW = textSize.width;
CGFloat introLabelH = textSize.height;
self.introF = CGRectMake(introLabelX, introLabelY, SCREEN_WIDTH - 40, introLabelH);
CGFloat cellHeight = 0;
if (_model.handleStatus) {
CGSize slabelSize = [self sizeWithString:_model.handleStatus font:NJTextFont maxSize:CGSizeMake(300, MAXFLOAT)];
CGFloat slabelW = slabelSize.width;
CGFloat slabelH = slabelSize.height;
self.sLabelF = CGRectMake(padding, CGRectGetMaxY(self.introF) +padding, slabelW, slabelH);
CGSize tlabelSize = [self sizeWithString:_model.applyDate font:VDTextFont maxSize:CGSizeMake(300, MAXFLOAT)];
CGFloat tlabelW = tlabelSize.width;
CGFloat tlabelH = tlabelSize.height;
self.tLabelF = CGRectMake(SCREEN_WIDTH-padding - tlabelW, CGRectGetMaxY(self.introF) +padding, tlabelW, tlabelH);
self.cellHeight = CGRectGetMaxY(self.tLabelF) + padding;
}else{
self.cellHeight = CGRectGetMaxY(self.introF) + padding;
}
}
/**
* 计算文本的宽高
*
* @param str 需要计算的文本
* @param font 文本显示的字体
* @param maxSize 文本显示的范围
*
* @return 文本占用的真实宽高
*/
- (CGSize)sizeWithString:(NSString *)str font:(UIFont *)font maxSize:(CGSize)maxSize
{
NSDictionary *dict = @{NSFontAttributeName : font};
// 如果将来计算的文字的范围超出了指定的范围,返回的就是指定的范围
// 如果将来计算的文字的范围小于指定的范围, 返回的就是真实的范围
CGSize size = [str boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:dict context:nil].size;
return size;
}
@end
至此,根据content的具体内容来计算宽高已经完成;
那么接下来就来到自定义的cell里来搞事情吧
#import
@class VDFrame;
@interface TCWorkMesSubTableViewCell : UITableViewCell
@property (nonatomic,strong)UILabel *mLableTitle;
@property(nonatomic, strong) UILabel *sLabel;
@property(nonatomic, strong) UILabel *tLabel;
@property (nonatomic ,strong)VDFrame *vDFrame;
+ (instancetype)cellWithTableView:(UITableView *)tableView;
@end
如上的cell中,定义了三个label,一个cell的类方法;
在cell的.m文件中
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *identifier = @"cell";
TCWorkMesSubTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[TCWorkMesSubTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
return cell;
}
也就是把cell的创建方法,不交给控制器,具体实现,交给他自己各司其职
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// 让自定义Cell和系统的cell一样, 一创建出来就拥有一些子控件提供给我们使用
UILabel *introLabel = [[UILabel alloc] init];
introLabel.font = [UIFont systemFontOfSize:13];
introLabel.numberOfLines = 0;
// introLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:introLabel];
self.mLableTitle = introLabel;
UILabel *statusLabel = [[UILabel alloc] init];
statusLabel.font = [UIFont systemFontOfSize:13];
statusLabel.numberOfLines = 0;
statusLabel.textColor = [UIColor redColor];
[self.contentView addSubview:statusLabel];
self.sLabel = statusLabel;
UILabel *timeLabel = [[UILabel alloc] init];
timeLabel.font = [UIFont systemFontOfSize:13];
timeLabel.numberOfLines = 0;
[self.contentView addSubview:timeLabel];
self.tLabel = timeLabel;
}
return self;
}
在setVDFrame里面对label们进行赋值,并且设置frame
- (void)setVDFrame:(VDFrame *)vDFrame{
_vDFrame = vDFrame;
// 1.给子控件赋值数据
[self settingData];
// 2.设置frame
[self settingFrame];
}
- (void)settingData
{
TCWorkMessageSamllList *intro = _vDFrame.model;
self.mLableTitle.text =intro.content;
self.sLabel.text = intro.handleStatus;
self.tLabel.text = intro.applyDate;
}
- (void)settingFrame
{
self.mLableTitle.frame = self.vDFrame.introF;
self.sLabel.frame = self.vDFrame.sLabelF;
self.tLabel.frame = self.vDFrame.tLabelF;
}
没错离成功还差一小步,
现在,可以把目光转移回控制器的UItableview的代理方法里了
来到
cellForRowAtIndexPath的方法里做一件很爽的事
cell.vDFrame = self.contentFrame[indexPath.row];
一句话搞定,
来到heightForRowAtIndexPath里
VDFrame *vdF = self.contentFrame[indexPath.row];
return vdF.cellHeight;
至此,
自适应完结.,当跟进自己的需求进行添砖加瓦如上frame以及cell所做即可.