在上一篇中,我们已经把首页微博显示出来了,但还有很多细节,需要我们去调整的。这一章中,我们将处理好时间,配图,工具框及转发微博等小细节的功能。
时间处理
第一步:定义一个时间的类别,用于判断是昨天、今天等。
NSDate+Time.h
#import <Foundation/Foundation.h> @interface NSDate (Time) /** * 判断某个时间是否为今年 */ - (BOOL)isThisYear; /** * 判断某个时间是否为昨天 */ - (BOOL)isYesterday; /** * 判断某个时间是否为今天 */ - (BOOL)isToday; @end
NSDate+Time.m
// // NSDate+Time.m // Weibo // // Created by jiangys on 15/10/25. // Copyright © 2015年 Jiangys. All rights reserved. // #import "NSDate+Time.h" @implementation NSDate (Time) /** * 判断某个时间是否为今年 */ - (BOOL)isThisYear { NSCalendar *calendar = [NSCalendar currentCalendar]; // 获得某个时间的年月日时分秒 NSDateComponents *dateCmps = [calendar components:NSCalendarUnitYear fromDate:self]; NSDateComponents *nowCmps = [calendar components:NSCalendarUnitYear fromDate:[NSDate date]]; return dateCmps.year == nowCmps.year; } /** * 判断某个时间是否为昨天 */ - (BOOL)isYesterday { NSDate *now = [NSDate date]; // date == 2014-04-30 10:05:28 --> 2014-04-30 00:00:00 // now == 2014-05-01 09:22:10 --> 2014-05-01 00:00:00 NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; fmt.dateFormat = @"yyyy-MM-dd"; // 2014-04-30 NSString *dateStr = [fmt stringFromDate:self]; // 2014-10-18 NSString *nowStr = [fmt stringFromDate:now]; // 2014-10-30 00:00:00 NSDate *date = [fmt dateFromString:dateStr]; // 2014-10-18 00:00:00 now = [fmt dateFromString:nowStr]; NSCalendar *calendar = [NSCalendar currentCalendar]; NSCalendarUnit unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay; NSDateComponents *cmps = [calendar components:unit fromDate:date toDate:now options:0]; return cmps.year == 0 && cmps.month == 0 && cmps.day == 1; } /** * 判断某个时间是否为今天 */ - (BOOL)isToday { NSDate *now = [NSDate date]; NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; fmt.dateFormat = @"yyyy-MM-dd"; NSString *dateStr = [fmt stringFromDate:self]; NSString *nowStr = [fmt stringFromDate:now]; return [dateStr isEqualToString:nowStr]; } @end
第二步:在Status.m中重写时间的属性
/** * 时间处理,重写 1.今年 1> 今天 * 1分内: 刚刚 * 1分~59分内:xx分钟前 * 大于60分钟:xx小时前 2> 昨天 * 昨天 xx:xx 3> 其他 * xx-xx xx:xx 2.非今年 1> xxxx-xx-xx xx:xx */ - (NSString *)created_at { NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; // 如果是真机调试,转换这种欧美时间,需要设置locale fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; // 设置日期格式(声明字符串里面每个数字和单词的含义) // E:星期几 // M:月份 // d:几号(这个月的第几天) // H:24小时制的小时 // m:分钟 // s:秒 // y:年 fmt.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // _created_at = @"Tue Sep 30 17:06:25 +0600 2014"; // 微博的创建日期 NSDate *createDate = [fmt dateFromString:_created_at]; // 当前时间 NSDate *now = [NSDate date]; // 日历对象(方便比较两个日期之间的差距) NSCalendar *calendar = [NSCalendar currentCalendar]; // NSCalendarUnit枚举代表想获得哪些差值 NSCalendarUnit unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; // 计算两个日期之间的差值 NSDateComponents *cmps = [calendar components:unit fromDate:createDate toDate:now options:0]; if ([createDate isThisYear]) { // 今年 if ([createDate isYesterday]) { // 昨天 fmt.dateFormat = @"昨天 HH:mm"; return [fmt stringFromDate:createDate]; } else if ([createDate isToday]) { // 今天 if (cmps.hour >= 1) { return [NSString stringWithFormat:@"%d小时前", (int)cmps.hour]; } else if (cmps.minute >= 1) { return [NSString stringWithFormat:@"%d分钟前", (int)cmps.minute]; } else { return @"刚刚"; } } else { // 今年的其他日子 fmt.dateFormat = @"MM-dd HH:mm"; return [fmt stringFromDate:createDate]; } } else { // 非今年 fmt.dateFormat = @"yyyy-MM-dd HH:mm"; return [fmt stringFromDate:createDate]; } }
这样,就可以显示出来了,但会有问题,就是不断的下拉刷新时,本来显示“刚刚”字样的,刷新后,可能变为“1分钟前”。由于“刚刚”的字体size不变化,因而显示“1分钟前”会导致显示不全。改进方法
/** 时间 */ // self.timeLabel.text = status.created_at; // self.timeLabel.frame = statusFrame.timeLabelF; /** 来源 */ // self.sourceLabel.text = status.source; // self.sourceLabel.frame = statusFrame.sourceLabelF; /** 时间 */ NSString *time = status.created_at; CGFloat timeX = statusFrame.nameLabelF.origin.x; CGFloat timeY = CGRectGetMaxY(statusFrame.nameLabelF) + StatusCellBorderW; CGSize timeSize = [time sizeWithFont:StatusCellTimeFont]; self.timeLabel.frame = (CGRect){{timeX, timeY}, timeSize}; self.timeLabel.text = time; /** 来源 */ CGFloat sourceX = CGRectGetMaxX(self.timeLabel.frame) + StatusCellBorderW; CGFloat sourceY = timeY; CGSize sourceSize = [status.source sizeWithFont:StatusCellSourceFont]; self.sourceLabel.frame = (CGRect){{sourceX, sourceY}, sourceSize}; self.sourceLabel.text = status.source;
这样,每重新加载cell时,都重新计算一遍时间和来源的size。
效果:
配图
第一步:通过查看新浪微博的API,我们可以看到,返回的配图是一个集合。先定义好集合里的模型
Photo.h
#import <Foundation/Foundation.h> @interface Photo : NSObject /** 缩略图地址 */ @property (nonatomic, copy) NSString *thumbnail_pic; @end
第二步:在Status模型了,添加配图的集合属性,并指定,里面的集合是由Photo这个模型来构成的。
Status.h
/** 微博配图地址。多图时返回多图链接。无配图返回“[]” */ @property (nonatomic, strong) NSArray *pic_urls;
Status.m ,需要引用 #import "MJExtension.h"
+ (NSDictionary *)objectClassInArray { return @{@"pic_urls" : [Photo class]}; }
第三步:拿到数据之后,接下来要做的事情
- 定义一个配图的View
- 定义一个装了该微博所有的配图View的容器
- 往容器里填充配图
- 计算这个容器的宽高
1.定义一个配图的View
StatusPhotoView.h
#import <UIKit/UIKit.h> @class Photo; @interface StatusPhotoView : UIImageView /** 图片模型 */ @property (nonatomic, strong) Photo *photo; @end
StatusPhotoView.m
// // StatusPhotoView.m // Weibo // // Created by jiangys on 15/10/25. // Copyright © 2015年 Jiangys. All rights reserved. // #import "StatusPhotoView.h" #import "Photo.h" #import "UIImageView+WebCache.h" @interface StatusPhotoView() @property (nonatomic, weak) UIImageView *gifView; @end @implementation StatusPhotoView - (UIImageView *)gifView { if (!_gifView) { UIImage *image = [UIImage imageNamed:@"timeline_image_gif"]; UIImageView *gifView = [[UIImageView alloc] initWithImage:image]; [self addSubview:gifView]; self.gifView = gifView; } return _gifView; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 内容模式 self.contentMode = UIViewContentModeScaleAspectFill; // 超出边框的内容都剪掉 self.clipsToBounds = YES; } return self; } - (void)setPhoto:(Photo *)photo { _photo = photo; // 设置图片 [self sd_setImageWithURL:[NSURL URLWithString:photo.thumbnail_pic] placeholderImage:[UIImage imageNamed:@"timeline_image_placeholder"]]; // 显示隐藏gif控件 // 判断是够以gif或者GIF结尾 self.gifView.hidden = ![photo.thumbnail_pic.lowercaseString hasSuffix:@"gif"]; } - (void)layoutSubviews { [super layoutSubviews]; self.gifView.x = self.width - self.gifView.width; self.gifView.y = self.height - self.gifView.height; } @end
2.定义一个装了该微博所有的配图View的容器
StatusPhotosView.h
#import <UIKit/UIKit.h> @interface StatusPhotosView : UIView /** 图片模型 */ @property (nonatomic, strong) NSArray *photos; /** * 根据图片个数计算相册的尺寸 */ + (CGSize)sizeWithCount:(NSUInteger)count; @end
StatusPhotosView.m
// // StatusPhotosView.m // Weibo // // Created by jiangys on 15/10/25. // Copyright © 2015年 Jiangys. All rights reserved. // #import "StatusPhotosView.h" #import "StatusPhotoView.h" #import "Photo.h" #define StatusPhotoWH 70 #define StatusPhotoMargin 10 #define StatusPhotoMaxCol(count) ((count==4)?2:3) @implementation StatusPhotosView - (void)setPhotos:(NSArray *)photos { _photos = photos; NSUInteger photosCount = photos.count; // 创建足够多的图片控制 while (self.subviews.count < photosCount) { StatusPhotoView *photoView = [[StatusPhotoView alloc] init]; [self addSubview:photoView]; } // 遍历所有的图片控件,设置图片 for (int i = 0; i < self.subviews.count; i++) { StatusPhotoView *photoView = self.subviews[i]; if (i < photosCount) { photoView.photo = photos[i]; photoView.hidden = NO; } else{ photoView.hidden=YES; } } } - (void)layoutSubviews { [super layoutSubviews]; // 设置图片的尺寸和位置 NSUInteger photosCount = self.photos.count; int maxCol = StatusPhotoMaxCol(photosCount); for (int i = 0; i<photosCount; i++) { StatusPhotoView *photoView = self.subviews[i]; int col = i % maxCol; photoView.x = col * (StatusPhotoWH + StatusPhotoMargin); int row = i / maxCol; photoView.y = row * (StatusPhotoWH + StatusPhotoMargin); photoView.width = StatusPhotoWH; photoView.height = StatusPhotoWH; } } + (CGSize)sizeWithCount:(NSUInteger)count { // 最大列数(一行最多有多少列) int maxCols = StatusPhotoMaxCol(count); NSUInteger cols = (count >= maxCols)? maxCols : count; CGFloat photosW = cols * StatusPhotoWH + (cols - 1) * StatusPhotoMargin; // 行数 NSUInteger rows = (count + maxCols - 1) / maxCols; CGFloat photosH = rows * StatusPhotoWH + (rows - 1) * StatusPhotoMargin; return CGSizeMake(photosW, photosH); } @end
3.StatusFrame.m里计算配图的宽高
/** 配图 */ CGFloat originalH = 0; if (status.pic_urls.count) { // 有配图 CGFloat photosX = contentX; CGFloat photosY = CGRectGetMaxY(self.contentLabelF) + StatusCellBorderW; CGSize photosSize = [StatusPhotosView sizeWithCount:status.pic_urls.count]; _photosViewF = (CGRect){{photosX, photosY}, photosSize}; originalH = CGRectGetMaxY(self.photosViewF) + StatusCellBorderW; } else { // 没配图 originalH = CGRectGetMaxY(self.contentLabelF) + StatusCellBorderW; }
4.在StatusCell.m里创建定义容器,初始化容器并赋值
/** 配图 */ @property (nonatomic, weak) StatusPhotosView *photosView;
初始化容器initWithStyle
/** 配图 */ StatusPhotosView *photosView = [[StatusPhotosView alloc] init]; [originalView addSubview:photosView]; self.photosView = photosView;
赋值setStatusFrame
/** 配图 */ if (status.pic_urls.count) { self.photosView.frame = statusFrame.photosViewF; self.photosView.photos = status.pic_urls; self.photosView.hidden = NO; } else { self.photosView.hidden = YES; }
效果如下图: