iOS开发项目篇—46时间和来源的处理(cell的复用问题)
一、简单说明
1.存在的问题:
2.问题描述:
刷新微博界面后,展示的最新的微博数据时间显示为“刚刚”,在项目中对时间进行设计的时候,如果是在1分钟之内发表的,那么显示为“刚刚”。查看后面的微博数据后,回过头来(1分钟已经过去了),此时之前显示为“刚刚”的微博,应该显示XX分钟以前,确实显示了,但是时间的frame不正确(此时的frame=="刚刚"两个字的frame)。
提示:cell的复用问题,为了提升性能节省内存,cell在设计的时候是只创建显示到眼前【界面】上的cell,当滚动的时候,复用之前创建的cell。
3.问题出在了哪里?
根据展示的效果,时间从“刚刚”--》“XX分钟之前”,可以知道代码设计部分已经完成了实时对“时间”数据进行更新的功能(总是拿微博发表的时间和当前的时间进行比较,再显示提示的时间信息),但是frame却没有被再次计算,还是之前的数值,也就是说在整个过程中,时间的显示【“刚刚” “XX分钟前” “一小时前”】等是不断刷新计算的,而显示时间的frame在程序中只被设置了一个初值,之后并没有同步的进行设置。
4.简单说明:显示时间的label的宽度永远是“刚刚”这两个汉字的宽度。
二、解决cell的复用产生的问题
显示时间的label的frame不能只计算一次,也应该要实时的进行更新。
1.在原创微博frame中再计算时间和来源的frame没有意义,删除相关代码。
提示:因为微博来源的frame和时间label的frame有直接的关系,所以连带着也应该计算来源的frame。
YYStatusOriginalFrame.h文件
1 // 2 // YYStatusOriginalFrame.h 3 // 4 5 #import <Foundation/Foundation.h> 6 7 @class YYStatusModel; 8 @interface YYStatusOriginalFrame : NSObject 9 /** 10 * 微博数据模型 11 */ 12 @property(nonatomic,strong)YYStatusModel *status; 13 14 /** 昵称 */ 15 @property (nonatomic, assign) CGRect nameFrame; 16 /** 正文 */ 17 @property (nonatomic, assign) CGRect textFrame; 18 ///** 来源 */ 19 //@property (nonatomic, assign) CGRect sourceFrame; 20 ///** 时间 */ 21 //@property (nonatomic, assign) CGRect timeFrame; 22 /** 头像 */ 23 @property (nonatomic, assign) CGRect iconFrame; 24 /** 会员图标 */ 25 @property (nonatomic, assign) CGRect vipViewFrame; 26 27 /** 自己的frame */ 28 @property (nonatomic, assign) CGRect frame; 29 @end
2.需要时刻根据现在的时间计算,显示时间和微博来源的label的frame
YYStatusOriginal.m文件
1 // 2 // YYStatusOriginal.m 3 // 4 5 #import "YYStatusOriginalView.h" 6 #import "YYStatusOriginalFrame.h" 7 #import "YYUserModel.h" 8 #include "YYStatusModel.h" 9 #import "UIImageView+WebCache.h" 10 11 @interface YYStatusOriginalView () 12 /** 13 * 头像 14 */ 15 @property(nonatomic,weak)UIImageView *iconView; 16 /** 17 * 昵称 18 */ 19 @property(nonatomic,weak)UILabel *nameLabel; 20 /** 21 * 微博的正文 22 */ 23 @property(nonatomic,weak)UILabel *textLabel; 24 /** 25 * 时间 26 */ 27 @property(nonatomic,weak)UILabel *timeLabel; 28 /** 29 * 来源 30 */ 31 @property(nonatomic,weak)UILabel *sourceLabel; 32 /** 33 * 会员图标 34 */ 35 @property(nonatomic,weak)UIImageView *vipView; 36 37 @end 38 @implementation YYStatusOriginalView 39 40 - (id)initWithFrame:(CGRect)frame 41 { 42 self = [super initWithFrame:frame]; 43 if (self) { 44 //初始化子控件 45 //1.头像 46 UIImageView *iconView=[[UIImageView alloc]init]; 47 [self addSubview:iconView]; 48 self.iconView=iconView; 49 50 //2.昵称 51 UILabel *nameLabel=[[UILabel alloc]init]; 52 nameLabel.font=YYStatusOrginalNameFont; 53 [self addSubview:nameLabel]; 54 self.nameLabel=nameLabel; 55 56 //3.正文 57 UILabel *textLabel=[[UILabel alloc]init]; 58 textLabel.font=YYStatusOrginalTextFont; 59 textLabel.numberOfLines=0; 60 [self addSubview:textLabel]; 61 self.textLabel=textLabel; 62 63 //4.时间 64 UILabel *timeLabel=[[UILabel alloc]init]; 65 timeLabel.font=YYStatusOrginalTimeFont; 66 timeLabel.textColor=[UIColor orangeColor]; 67 [self addSubview:timeLabel]; 68 self.timeLabel=timeLabel; 69 70 //5.来源 71 UILabel *sourceLabel=[[UILabel alloc]init]; 72 sourceLabel.font=YYStatusOrginalSourceFont; 73 sourceLabel.textColor=[UIColor lightGrayColor]; 74 [self addSubview:sourceLabel]; 75 self.sourceLabel=sourceLabel; 76 77 //6.会员图标 78 UIImageView *vipView=[[UIImageView alloc]init]; 79 //会员图标应该设置保持原来的尺寸,垂直居中 80 vipView.contentMode=UIViewContentModeCenter; 81 [self addSubview:vipView]; 82 self.vipView=vipView; 83 84 } 85 return self; 86 } 87 88 -(void)setOriginalFrame:(YYStatusOriginalFrame *)originalFrame 89 { 90 _originalFrame=originalFrame; 91 92 //设置自己的frame 93 self.frame=originalFrame.frame; 94 95 //取出模型数据 96 YYStatusModel *status=originalFrame.status; 97 98 //设置头像的frame 99 [self.iconView setImageWithURL:[NSURL URLWithString:status.user.profile_image_url] placeholderImage:[UIImage imageWithName:@"avatar_default_small"]]; 100 self.iconView.frame=originalFrame.iconFrame; 101 102 //设置昵称的frame 103 self.nameLabel.text=status.user.name; 104 //注意循环利用的问题 105 if (status.user.isVip) { 106 self.nameLabel.textColor=[UIColor orangeColor]; 107 self.vipView.hidden=NO; 108 self.vipView.frame=originalFrame.vipViewFrame; 109 self.vipView.image=[UIImage imageWithName:[NSString stringWithFormat:@"common_icon_membership_level%d",status.user.mbrank]]; 110 }else 111 { 112 self.nameLabel.textColor=[UIColor blackColor]; 113 self.vipView.hidden=YES; 114 } 115 self.nameLabel.frame=originalFrame.nameFrame; 116 117 //设置正文的frame 118 self.textLabel.text=status.text; 119 self.textLabel.frame=originalFrame.textFrame; 120 121 #warning 需要时刻根据现在的时间计算,显示时间和微博来源的label的frame 122 //设置时间的frame 123 NSString *timeStr=status.created_at; 124 self.timeLabel.text=timeStr; 125 126 CGFloat timeX=CGRectGetMinX(self.nameLabel.frame); 127 CGFloat timeY=CGRectGetMaxY(self.nameLabel.frame)+YYCellStatusInset*0.5; 128 CGSize timeSize=[timeStr sizeWithFont:YYStatusOrginalTimeFont]; 129 self.timeLabel.frame=(CGRect){{timeX,timeY},timeSize}; 130 131 //设置来源的frame 132 self.sourceLabel.text=status.source; 133 134 CGFloat sourcceX=CGRectGetMaxX( self.timeLabel.frame)+YYCellStatusInset; 135 CGFloat sourcceY=timeY; 136 CGSize sourcceSize=[status.source sizeWithFont:YYStatusOrginalSourceFont]; 137 self.sourceLabel.frame=(CGRect){{sourcceX,sourcceY},sourcceSize}; 138 139 } 140 @end
3.修复了这个问题的显示效果:
4.性能优化处理
说明:
(1)每次刷新,都需要对时间进行处理,但是微博的来源是固定的,没必要每次刷新的时候就调用一次浪费掉宝贵的内存空间。
(2)且在设置微博来源的时候,是通过截取并拼接字符串这样的方式进行的,而这恰是特别耗时的操作。没必要每次调用self.sourceLabel.text=status.source;的时候都去拼接字符串(这个方法调用非常频繁,每当一个cell进入到视野的时候,都会计算一遍)。
(3)计算时间,每次得出的结果是不一样的。但是计算微博来源,每次截取字符串都是固定的,是一样的。
(4)set方法只会在字典转模型的那一刻调用(因为要把字典属性赋值给模型属性),get方法时刻都在调用。
(5)解决方法:删除get方法,重写一个相应的set方法
YYStatusModel.m文件
1 // 2 // YYStatusModel.m 3 // 4 5 #import "YYStatusModel.h" 6 #import "MJExtension.h" 7 #import "YYPhotoModel.h" 8 #import "NSDate+MJ.h" 9 10 @implementation YYStatusModel 11 //+(instancetype)statusModelWithDict:(NSDictionary *)Dict 12 //{ 13 // YYStatusModel *model=[[self alloc]init]; 14 // model.text=Dict[@"text"]; 15 // 16 // model.user=[YYUserModel userModelWithDict:Dict[@"user"]]; 17 // 18 // //嵌套模型 19 // NSDictionary *retweetedDict = Dict[@"retweetedDict"]; 20 // if (retweetedDict) { 21 // model.retweeted_status=[YYStatusModel statusModelWithDict:retweetedDict]; 22 // } 23 // return model; 24 //} 25 26 -(NSDictionary *)objectClassInArray 27 { 28 return @{@"pic_urls" : [YYPhotoModel class]}; 29 } 30 31 //重写创建时间的get方法 32 /**_created_at==Sat Jul 19 15:24:04 +0800 2014*/ 33 -(NSString *)created_at 34 { 35 // [email protected]"Sat Jul 18 15:24:04 +0800 2014"; 36 NSDateFormatter *fmt=[[NSDateFormatter alloc]init]; 37 //时间格式 38 fmt.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; 39 //获得尾部发布的具体时间 40 NSDate *creatDate = [fmt dateFromString:_created_at]; 41 42 //判断是否为今年 43 if (creatDate.isThisYear) {//今年 44 if (creatDate.isToday) { 45 //获得微博发布的时间与当前时间的差距 46 NSDateComponents *cmps=[creatDate deltaWithNow]; 47 if (cmps.hour>=1) {//至少是一个小时之前发布的 48 return [NSString stringWithFormat:@"%d小时前",cmps.hour]; 49 }else if(cmps.minute>=1){//1~59分钟之前发布的 50 return [NSString stringWithFormat:@"%d分钟前",cmps.minute]; 51 }else{//1分钟内发布的 52 return @"刚刚"; 53 } 54 }else if(creatDate.isYesterday){//昨天发的 55 fmt.dateFormat=@"昨天 HH:mm"; 56 return [fmt stringFromDate:creatDate]; 57 }else{//至少是前天发布的 58 fmt.dateFormat=@"MM-dd HH:mm"; 59 return [fmt stringFromDate:creatDate]; 60 } 61 }else // 往年 62 { 63 fmt.dateFormat=@"yyyy-MM-dd"; 64 return [fmt stringFromDate:creatDate]; 65 } 66 } 67 /**_source== <a href="http://app.weibo.com/t/feed/3j6BDx" rel="nofollow">孔明社交管理</a>*/ 68 //所需要的:孔明社交管理 69 //-(NSString *)source 70 //{ 71 // //截取字符串,获得子串 72 // //截取的范围 73 // NSRange range; 74 // //截取的位置:第一个>之后 75 // range.location=[_source rangeOfString:@">"].location+1; 76 // //截取的长度:第二个<的位置到第一个>之间的长度 77 // range.length=[_source rangeOfString:@"</"].location-range.location; 78 // 79 // //开始截取 80 // NSString *subSource=[_source substringWithRange:range]; 81 // //头部拼接“来自” 82 // return [NSString stringWithFormat:@"来自%@",subSource]; 83 //} 84 85 -(void)setSource:(NSString *)source 86 { 87 //截取字符串,获得子串 88 //截取的范围 89 NSRange range; 90 //截取的位置:第一个>之后 91 range.location=[source rangeOfString:@">"].location+1; 92 //截取的长度:第二个<的位置到第一个>之间的长度 93 range.length=[source rangeOfString:@"</"].location-range.location; 94 95 //开始截取 96 NSString *subSource=[source substringWithRange:range]; 97 //头部拼接“来自” 98 _source = [NSString stringWithFormat:@"来自%@",subSource]; 99 } 100 101 @end
提示:KVC的本质也是调用set方法。
iOS开发项目篇—46时间和来源的处理(cell的复用问题)