首先看一下效果图(不带转发微博和带转发微博):
一、微博Cell布局分析
微博Cell的详细布局如下图所示,其主要控件有:头像、昵称、微博时间、来源、微博正文。如果有被转发微博,还包括被转发微博的整体框架、转发微博的用户昵称、转发微博的内容以及Cell下方的操作条(转发、评论、赞)等。
二、用户头像的封装——HeadIconView类
用过新浪微博APP的朋友们都知道,微博用户一共有4种认证:无认证、微博达人、个人vip、企业vip。针对不同的认证,微博会在用户头像的右下角显示不同的图标。同时,应用中不同的地方用户头像的尺寸大小也不尽相同,为了应用更好的扩展性和后期维护性,需要对用户头像进行封装。
用户头像的显示中包括头像、认证图标。HeadIconView类应继承自UIView,其持有User对象,并重写setUser方法,当HeadIconView拿到User对象后,根据User中的
profile_image_url等信息设置用户的头像、头像大小以及认证图标等。
HeadIconView.h
#import
typedef enum{
kHeadIconTypeSmall,
kHeadIconTypeDefault,
kHeadIconTypeBig
}HeadIconType;
@class User;
@interface HeadIconView : UIView
@property (strong, nonatomic)User *user;
@property (assign, nonatomic)HeadIconType headIconType;
+(CGSize)headIconSizeWithType:(HeadIconType)iconType;
-(void)setUser:(User *)user type:(HeadIconType)type;
@end
HeadIconView.m
#import "HeadIconView.h"
#import "Macro.h"
#import "User.h"
#import "HttpTool.h"
@interface HeadIconView()
{
UIImageView *_headIcon; // 头像图片
UIImageView *_verifyIcon; // 认证图标
NSString *_placeHolder; // 占位图片名称
}
@end
@implementation HeadIconView
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 1. 用户头像图片
_headIcon = [[UIImageView alloc]init];
[self addSubview:_headIcon];
// 2. 认证图标
_verifyIcon = [[UIImageView alloc]init];
[self addSubview:_verifyIcon];
}
return self;
}
-(void)setUser:(User *)user
{
_user = user;
// 1. 设置用户头像
[HttpTool downloadImage:user.profile_image_url placeholderImage:[UIImage imageNamed:_placeHolder] imageView:_headIcon];
// 2. 设置认证图标
NSString *verifiedIcon = nil;
switch (user.verified_type) {
case kVerifiedTypeNone:
_verifyIcon.hidden = YES;
break;
case kVerifiedTypeDaren:
_verifyIcon.hidden = NO;
verifiedIcon = @"avatar_grassroot.png";
break;
case kVerifiedTypePersonal:
_verifyIcon.hidden = NO;
verifiedIcon = @"avatar_vip.png";
break;
default:
_verifyIcon.hidden = NO;
verifiedIcon = @"avatar_enterprise_vip.png";
break;
}
if (verifiedIcon) {
_verifyIcon.hidden = NO;
_verifyIcon.image = [UIImage imageNamed:verifiedIcon];
}
}
-(void)setUser:(User *)user type:(HeadIconType)type
{
self.headIconType = type;
self.user = user;
}
#pragma mark 设置头像和认证图标的类型
-(void)setHeadIconType:(HeadIconType)headIconType
{
_headIconType = headIconType;
// 1. 判断认证类型
CGSize headIconSize;
switch (headIconType) {
case kHeadIconTypeSmall:
headIconSize = CGSizeMake(kHeadIconSmallW, kHeadIconSmallH);
_placeHolder = @"avatar_default_small.png";
break;
case kHeadIconTypeDefault:
headIconSize = CGSizeMake(kHeadIconDefaultW, kHeadIconDefaultH);
_placeHolder = @"avatar_default.png";
break;
case kHeadIconTypeBig:
headIconSize = CGSizeMake(kHeadIconBigW, kHeadIconBigH);
_placeHolder = @"avatar_default_big.png";
break;
}
// 2. 设置frame
_headIcon.frame = (CGRect){CGPointZero, headIconSize};
_verifyIcon.bounds = CGRectMake(0, 0, kVerifyIconW, kVerifyIconH);
_verifyIcon.center = CGPointMake(headIconSize.width, headIconSize.height);
CGFloat width = headIconSize.width + kVerifyIconW*0.5;
CGFloat height = headIconSize.height + kVerifyIconH*0.5;
self.bounds = CGRectMake(0, 0, width, height);
}
#pragma mark 设置头像的大小
+(CGSize)headIconSizeWithType:(HeadIconType)iconType
{
CGSize headIconSize;
switch (iconType) {
case kHeadIconTypeSmall:
headIconSize = CGSizeMake(kHeadIconSmallW, kHeadIconSmallH);
break;
case kHeadIconTypeDefault:
headIconSize = CGSizeMake(kHeadIconDefaultW, kHeadIconDefaultH);
break;
case kHeadIconTypeBig:
headIconSize = CGSizeMake(kHeadIconBigW, kHeadIconBigH);
break;
}
CGFloat width = headIconSize.width + kVerifyIconW * 0.5;
CGFloat height = headIconSize.height + kVerifyIconH*0.5;
return CGSizeMake(width, height);
}
@end
三、微博配图的封装(ImageItemView和ImageListView)
当一条微博有配图时,我们需要判断微博配图的数目并将这些配图以正确的方式显示出来。
这些配图可能是Gif图片或普通静态图片,当拿到微博配图数据时,要判断其图片类型,并设置相应的gif标志。首先对单个配图进行封装,ImageItemView类设置一个NSString类型的对象——imageUrl,并重写setImageUrl方法,当拿到图片的URL时,自动加载图片并判断图片是否gif。
ImageItemView.h
#import
@interface ImageItemView : UIImageView
@property (copy, nonatomic)NSString *imageUrl;
@end
#import "ImageItemView.h"
#import "HttpTool.h"
@interface ImageItemView()
{
UIImageView *_gifFlagImage;
}
@end
@implementation ImageItemView
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
_gifFlagImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"timeline_image_gif.png"]];
[self addSubview:_gifFlagImage];
}
return self;
}
-(void)setImageUrl:(NSString *)imageUrl
{
_imageUrl = imageUrl;
// 1. 加载图片
[HttpTool downloadImage:imageUrl placeholderImage:[UIImage imageNamed:@"timeline_image_loading.png"] imageView:self];
// 2. 判断该图片是否时gif
_gifFlagImage.hidden = ![imageUrl.lowercaseString hasSuffix:@"gif"];
}
-(void)setFrame:(CGRect)frame
{
// 设置gifView的位置
CGRect gifViewFrame = _gifFlagImage.frame;
gifViewFrame.origin.x = frame.size.width - gifViewFrame.size.width;
gifViewFrame.origin.y = frame.size.height - gifViewFrame.size.height;
_gifFlagImage.frame = gifViewFrame;
[super setFrame:frame];
}
@end
由于微博的配图数目范围为0~9,对于不同数目的配图单独处理其布局显然是不合适的,因此,需要也需要对配图类进行封装。ImageListView类中有一个NSArray类型的对象用于接收外部传入的微博配图信息(配图的URL),并根据配图的数目自动确定显示配图控件的CGSize大小。
ImageListView.h
#import
@interface ImageListView : UIView
@property (strong, nonatomic)NSArray *imageUrlList;
+(CGSize)imageListSizeWithCount:(int)count;
@end
ImageListView.m
#define kCount 9
#define kSingleImageW 120
#define kSingleImageH 120
#define kMultiImageW 80
#define kMultiImageH 80
#define kMargin 5
#import "ImageListView.h"
#import "UIImageView+WebCache.h"
#import "ImageItemView.h"
@implementation ImageListView
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 先把有可能显示的空间都加入
for (int i = 0; i < kCount; i++) {
ImageItemView *imageView = [[ImageItemView alloc]init];
[self addSubview:imageView];
}
}
return self;
}
-(void)setImageUrlList:(NSArray *)imageUrlList
{
_imageUrlList = imageUrlList;
int count = (int)imageUrlList.count;
for (int i = 0; i < kCount; i++) {
// 1. 取出对应位置的子控件
ImageItemView *child = self.subviews[i];
// 2. 判断配图的数量
if (i >= count) {
child.hidden = YES; // 多余的位置隐藏
}else{
child.hidden = NO;
// 3. 设置图片
child.imageUrl = imageUrlList[i][@"thumbnail_pic"];
// 4. 设置frame
if (count == 1) {
child.contentMode = UIViewContentModeScaleAspectFit; // 自适应图片的大小
child.frame = CGRectMake(0, 0, kSingleImageW, kSingleImageH);
}else{
int temp = (count == 4 ? 2 : 3);
int row = i / temp;
int column = i % temp;
CGFloat x = (kMultiImageW + kMargin)*column;
CGFloat y = (kMultiImageH + kMargin)*row;
child.frame = CGRectMake(x, y, kMultiImageW, kMultiImageH);
child.contentMode = UIViewContentModeScaleAspectFill; // 图片适应内容拉伸
child.clipsToBounds = YES; // 剪去图片的多余部分
}
}
}
}
+(CGSize)imageListSizeWithCount:(int)count
{
// 1. 只有一张配图
if (count == 1) {
return CGSizeMake(kSingleImageW, kSingleImageH);
}
// 2. 至少有两张配图
int countRow = ((count == 4) ? 2 : 3);
// 总行数
int rows = (count + countRow - 1) / countRow;
// 总列数
int columns = (count >= 3 ? 3 : 2);
CGFloat width = columns * kMultiImageW + (columns - 1) * kMargin;
CGFloat height = rows * kMultiImageH + (rows - 1) * kMargin;
return CGSizeMake(width, height);
}
@end
四、微博底部状态条(CellOptionBar)
状态条中得数据是由微博(Status)的信息得到的,CellOptionBar持有Status对象,并用status中的repost_count、comments_count和attitudes_count设置button上显示的数字。
CellOptionBar.h
#import
@class Status;
@interface CellOptionBar : UIImageView
@property (strong, nonatomic)Status *status;
@end
#import "CellOptionBar.h"
#import "Status.h"
#import "UIImage+Extended.h"
#import "NSString+Extended.h"
#import "Macro.h"
@interface CellOptionBar()
{
UIButton *_repostButton;
UIButton *_commentButton;
UIButton *_attitudeButton;
}
@end
@implementation CellOptionBar
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 设置自动伸缩,粘在cell的底部
//self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
// 设置可以接收用户点击
self.userInteractionEnabled = YES;
// 设置背景
self.image = [UIImage resizedImage:@"timeline_card_background.png"];
self.highlightedImage = [UIImage resizedImage:@"timeline_card_background_highlighted.png"];
// 添加底部的三个按钮
_repostButton = [self addButtonWithTitle:@"转发" iconImage:@"timeline_icon_retweet.png" backgroundImage:@"timeline_card_middle_background.png"buttonIndex:0];
_commentButton = [self addButtonWithTitle:@"评论" iconImage:@"timeline_icon_comment.png" backgroundImage:@"timeline_card_middle_background.png" buttonIndex:1];
_attitudeButton = [self addButtonWithTitle:@"赞" iconImage:@"timeline_icon_like_disable.png" backgroundImage:@"timeline_card_middle_background.png" buttonIndex:2];
}
return self;
}
-(UIButton *)addButtonWithTitle:(NSString*)title iconImage:(NSString*)iconImage backgroundImage:(NSString*)bgImage buttonIndex:(NSInteger)index
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
// 标题
[button setTitle:title forState:UIControlStateNormal];
// 图标
[button setImage:[UIImage imageNamed:iconImage] forState:UIControlStateNormal];
// 背景图片
[button setBackgroundImage:[UIImage resizedImage:bgImage] forState:UIControlStateNormal];
[button setBackgroundImage:[UIImage resizedImage:[bgImage fileNameAppend:@"_highlighted"]] forState:UIControlStateHighlighted];
// 文字颜色
[button setTitleColor:[UIColor colorWithWhite:0.647 alpha:1.000] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:12];
// 设置按钮的frame
CGFloat width = self.frame.size.width / 3;
button.frame = CGRectMake(width * index, 0, width, kCellBarHeight);
// 设置文字左边与图片的间距
button.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, 0);
[self addSubview:button];
if (index) {
UIImage * dividerImage = [UIImage imageNamed:@"timeline_card_bottom_line.png"];
UIImageView *divider = [[UIImageView alloc]initWithImage:dividerImage];
divider.center = CGPointMake(button.frame.origin.x, kCellBarHeight * 0.5);
[self addSubview:divider];
}
return button;
}
-(void)setButton:(UIButton*)button withTitle:(NSString*)title withCount:(int)count
{
if (count >= 10000) {
NSString *title = [NSString stringWithFormat:@"%.1f万", count / 10000.0];
title = [title stringByReplacingOccurrencesOfString:@".0" withString:@""];
[button setTitle:title forState:UIControlStateNormal];
}else if (count > 0){
[button setTitle:[NSString stringWithFormat:@"%d", count] forState:UIControlStateNormal];
}else{
[button setTitle:title forState:UIControlStateNormal];
}
}
-(void)setFrame:(CGRect)frame
{
frame.size.width = [UIScreen mainScreen].bounds.size.width;
frame.size.height = kCellBarHeight;
[super setFrame:frame];
}
-(void)setStatus:(Status *)status
{
_status = status;
// 1. 转发
[self setButton:_repostButton withTitle:@"转发" withCount:status.repost_count];
// 2. 评论
[self setButton:_commentButton withTitle:@"评论" withCount:status.comments_count];
// 3. 赞
[self setButton:_attitudeButton withTitle:@"赞" withCount:status.attitudes_count];
}
@end
首先,StatusCellFrame类负责计算每条微博中各个子控件的frame,它有一个Status类型的变量,拿到微博的详细数据后开始计算frame;
StatusCell持有StatusCellFrame对象,使用cell.statusCellFrame设置各个Cell的frame。
StatusCellFrame.h
#import
#import
@class Status;
@interface StatusCellFrame : NSObject
{
CGFloat _cellHeight;
CGRect _repostFrame; // 被转发微博的父控件
}
@property (strong, nonatomic)Status *status; // 微博数据
@property (readonly, nonatomic) CGFloat cellHeight;
@property (readonly, nonatomic) CGRect headIconFrame; // 头像的frame
@property (readonly, nonatomic) CGRect screenNameFrame;
@property (readonly, nonatomic) CGRect mbIconFrame;
@property (readonly, nonatomic) CGRect timeFrame;
@property (readonly, nonatomic) CGRect sourceFrame;
@property (readonly, nonatomic) CGRect textFrame;
@property (readonly, nonatomic) CGRect imageFrame;
@property (readonly, nonatomic) CGRect repostedFrame; // 被转发微博的父控件
@property (readonly, nonatomic) CGRect repostedScreenNameFrame;
@property (readonly, nonatomic) CGRect repostedTextFrame;
@property (readonly, nonatomic) CGRect repostedImageFrame;
@property (readonly, nonatomic) CGRect cellBarFrame;
@property (readonly, nonatomic) CGRect blankBarFrame;
@end
StatusCellFrame.m
#import "StatusCellFrame.h"
#import "Status.h"
#import "User.h"
#import "HeadIconView.h"
#import "ImageListView.h"
#import "Macro.h"
@implementation StatusCellFrame
-(void)setStatus:(Status *)status
{
_status = status;
// 拿到微博数据即计算其内部各控件的frame
// 整个cell的宽度
CGFloat cellWidth = [UIScreen mainScreen].bounds.size.width;
// 1. 头像
CGFloat headIconX = kCellBorderWidth_Cell;
CGFloat headIconY = kCellBorderWidth_Cell;
CGSize iconSize = [HeadIconView headIconSizeWithType:kHeadIconTypeSmall];
_headIconFrame = CGRectMake(headIconX, headIconY, iconSize.width, iconSize.height);
// 2. 昵称
CGFloat screenNameX = CGRectGetMaxX(_headIconFrame) + kCellBorderWidth;
CGFloat screenNameY = headIconY;
CGSize screenNameSize = [status.user.screenName sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kGlobalTitleSizeBig]}];
_screenNameFrame = (CGRect){{screenNameX, screenNameY}, screenNameSize};
// 3. 会员图标
if (status.user.mbType != kMBTypeNone) {
CGFloat mbIconX = CGRectGetMaxX(_screenNameFrame) + kCellBorderWidth;
CGFloat mbIconY = screenNameY + (screenNameSize.height - kMBIconH)*0.5;
_mbIconFrame = CGRectMake(mbIconX, mbIconY, kMBIconW, kMBIconH);
}
// 4. 时间
CGFloat timeX = screenNameX;
CGFloat timeY = CGRectGetMaxY(_screenNameFrame) + kCellBorderWidth;
CGSize timeSize = [status.create_at sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kGlobalTitleSizeSuperSmall]}];
_timeFrame = (CGRect) {{timeX, timeY}, timeSize};
// 5. 微博来源
CGFloat sourceX = CGRectGetMaxX(_timeFrame) + kCellBorderWidth;
CGFloat sourceY = timeY;
CGSize sourceSize = [status.source sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kGlobalTitleSizeSuperSmall]}];
_sourceFrame = (CGRect){{sourceX, sourceY}, sourceSize};
// 6. 微博内容
CGFloat textX = headIconX;
CGFloat maxY = MAX(CGRectGetMaxY(_sourceFrame), CGRectGetMaxY(_headIconFrame));
CGFloat textY = maxY + kCellBorderWidth;
CGSize textSize = [status.statusContent boundingRectWithSize:CGSizeMake(cellWidth - 2*kCellBorderWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kGlobalTitleSizeBig]} context:nil].size;
_textFrame = (CGRect) {{textX, textY}, textSize};
if (status.picUrls.count) {// 7. 判断是否有配图
CGFloat imageX = textX;
CGFloat imageY = CGRectGetMaxY(_textFrame) + kCellBorderWidth;
CGSize imageSize = [ImageListView imageListSizeWithCount:(int)status.picUrls.count];
_imageFrame = CGRectMake(imageX, imageY, imageSize.width, imageSize.height);
}else if (status.repostStatus){// 8. 有转发的微博
// 被转发微博的整体框架
CGFloat repostedX = 2;
CGFloat repostedY = CGRectGetMaxY(_textFrame) + kCellBorderWidth;
CGFloat repostedWidth = cellWidth - 4;
CGFloat repostedHeight = 0;
// 9. 被转发微博的昵称
CGFloat repostedScreenNameX = kCellBorderWidth_Cell;
CGFloat repostedScreenNameY = kCellBorderWidth_Cell;
NSString *name = [NSString stringWithFormat:@"@%@", status.repostStatus.user.screenName];
CGSize repostedScreenNameSize = [name sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kGlobalTitleSizeMiddle]}];
_repostedScreenNameFrame = (CGRect){{repostedScreenNameX, repostedScreenNameY}, repostedScreenNameSize};
// 9. 被转发微博的内容
CGFloat repostedTextX = repostedScreenNameX;
CGFloat repostedTextY = CGRectGetMaxY(_repostedScreenNameFrame) + kCellBorderWidth;
CGSize repostedTextSize = [status.repostStatus.statusContent boundingRectWithSize:CGSizeMake(repostedWidth - 2*kCellBorderWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:kGlobalTitleSizeMiddle]} context:nil].size;
_repostedTextFrame = (CGRect){{repostedTextX, repostedTextY}, repostedTextSize};
// 10. 被转发微博的配图
if (status.repostStatus.picUrls.count) {
CGFloat repostedImageX = repostedTextX;
CGFloat repostedImageY = CGRectGetMaxY(_repostedTextFrame) + kCellBorderWidth;
CGSize imageSize = [ImageListView imageListSizeWithCount:(int)status.repostStatus.picUrls.count];
_repostedImageFrame = CGRectMake(repostedImageX, repostedImageY, imageSize.width, imageSize.height);
// 11. 计算被转发微博的整体高度
repostedHeight += CGRectGetMaxY(_repostedImageFrame) + kCellBorderWidth;
}else{
repostedHeight += CGRectGetMaxY(_repostedTextFrame) + kCellBorderWidth;
}
_repostedFrame = CGRectMake(repostedX, repostedY, repostedWidth, repostedHeight);
}
// 12. 整个cell的高度
_cellHeight = kCellBarHeight + kCellMargin;
//_cellHeight = kCellBorderWidth + kCellMargin;
if (status.picUrls.count) {
_cellHeight += CGRectGetMaxY(_imageFrame) + kCellBorderWidth;
}else if (status.repostStatus){
_cellHeight += CGRectGetMaxY(_repostedFrame);
}else{
_cellHeight += CGRectGetMaxY(_textFrame) + kCellBorderWidth;
}
// 底部操作条
CGFloat cellbarX = 0;
CGFloat cellbarY = _cellHeight - kCellBarHeight - kCellMargin;
_cellBarFrame = CGRectMake(cellbarX, cellbarY, cellWidth, kCellBarHeight);
// 底部间距栏
CGFloat blankX = 0;
CGFloat blankY = _cellHeight - kCellMargin;
CGFloat blankWidth = cellWidth;
CGFloat blankHeight = 10;
_blankBarFrame = CGRectMake(blankX, blankY, blankWidth, blankHeight);
}
@end
StatusCell.h
#import
@class StatusCellFrame;
@class StatusCell;
@protocol StatusCellDelegate
@optional
-(void)StatusTap:(StatusCell *)cell;
-(void)RepostStatusTap:(StatusCell *)cell;
@end
@interface StatusCell : UITableViewCell
{
UIImageView *_background;
UIImageView *_selectedBackground;
UIImageView *_reposted; // 被转发的微博的父控件
}
@property (strong, nonatomic)StatusCellFrame *cellFrame;
@property (assign, nonatomic)id delegate;
@end
StatusCell.m
#import "StatusCell.h"
#import "HeadIconView.h"
#import "ImageListView.h"
#import "UIImage+Extended.h"
#import "Status.h"
#import "User.h"
#import "StatusCellFrame.h"
#import "Macro.h"
#import "CellOptionBar.h"
@interface StatusCell()
{
HeadIconView *_headIcon; // 头像
UILabel *_screenName; // 昵称
UIImageView *_mbIcon; // 会员图标
UILabel *_time; // 时间
UILabel *_source; // 来源
UILabel *_statusContent; // 微博内容
ImageListView *_images; // 微博配图
UILabel *_repostedScreenName; // 转发微博用户昵称
UILabel *_repostedStatusContent; // 转发微博内容
ImageListView *_repostedImages; // 转发微博配图
CellOptionBar *_cellBar;
UIImageView *_blankBar;
}
@end
@implementation StatusCell
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// 1. 添加当前微博的子控件
[self addAllSubViews];
// 2. 添加被转发微博的子控件
[self addRepostAllSubviews];
// 3. 设置背景
[self setBackground];
// 4. 底部的操作条(转发、评论、赞)
_cellBar = [[CellOptionBar alloc]init];
[self.contentView addSubview:_cellBar];
}
return self;
}
#pragma mark 设置cell的背景
-(void)setBackground
{
self.backgroundColor = [UIColor clearColor];
// 1. 默认背景
UIImageView *background = [[UIImageView alloc]init];
self.backgroundView = background;
_background = background;
_background.image = [UIImage resizedImage:@"common_card_background.png"];
// 2. 长按背景
UIImageView *selectedBackground = [[UIImageView alloc]init];
self.selectedBackgroundView = selectedBackground;
_selectedBackground = selectedBackground;
_selectedBackground.image = [UIImage resizedImage:@"common_card_background_highlighted.png"];
}
#pragma mark 添加cell中的子控件
-(void)addAllSubViews
{
// 1. 头像
_headIcon = [[HeadIconView alloc]init];
[self.contentView addSubview:_headIcon];
// 2. 昵称
_screenName = [[UILabel alloc]init];
_screenName.font = kScreenNameFont;
_screenName.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:_screenName];
// 3. 会员图标
_mbIcon = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"common_icon_membership.png"]];
[self.contentView addSubview:_mbIcon];
// 4. 时间
_time = [[UILabel alloc]init];
_time.font = kTimeFont;
_time.textColor = kColor(246, 165, 68);
_time.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:_time];
// 5. 来源
_source = [[UILabel alloc]init];
_source.font = kSourceFont;
_source.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:_source];
// 6. 微博内容
_statusContent = [[UILabel alloc]init];
_statusContent.font = kStatusContentFont;
_statusContent.numberOfLines = 0; // 换行
_statusContent.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:_statusContent];
// 7. 微博配图
_images = [[ImageListView alloc]init];
[self.contentView addSubview:_images];
// 8. cell间距图片
_blankBar = [[UIImageView alloc]init];
_blankBar.image = [UIImage resizedImage:@"timeline_card_bottom_background_highlighted.png"];
//_blankBar.backgroundColor = kGlobalBackgroundColor;
[self.contentView addSubview:_blankBar];
}
#pragma mark 添加被转发的微博的子控件
-(void)addRepostAllSubviews
{
UITapGestureRecognizer *repostStatusTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(repostStatusTap)];
// 1. 被转发微博的父控件
_reposted = [[UIImageView alloc]init];
_reposted.image = [UIImage resizedImage:@"timeline_retweet_background.png"];
_reposted.userInteractionEnabled = YES;
[_reposted addGestureRecognizer:repostStatusTap];
[self.contentView addSubview:_reposted];
// 2. 被转发微博的用户昵称
_repostedScreenName = [[UILabel alloc]init];
_repostedScreenName.font = kRepostedScreenNameFont;
_repostedScreenName.textColor = kRepostScreenNameColor;
_repostedScreenName.backgroundColor = [UIColor clearColor];
[_reposted addSubview:_repostedScreenName];
// 3. 被转发微博的内容
_repostedStatusContent = [[UILabel alloc]init];
_repostedStatusContent.font = kRepostedTextFont;
_repostedStatusContent.numberOfLines = 0;
_repostedStatusContent.backgroundColor = [UIColor clearColor];
[_reposted addSubview:_repostedStatusContent];
// 4. 被转发微博的配图
_repostedImages = [[ImageListView alloc]init];
[_reposted addSubview:_repostedImages];
}
-(void)setCellFrame:(StatusCellFrame *)cellFrame
{
_cellFrame = cellFrame;
Status *s = cellFrame.status;
// 1. 头像
[_headIcon setUser:s.user type:kHeadIconTypeSmall];
_headIcon.frame = cellFrame.headIconFrame;
// 2. 昵称
_screenName.frame = cellFrame.screenNameFrame;
_screenName.text = s.user.screenName;
// 2.1 判断是否会员
if (s.user.mbType == kMBTypeNone) {
_screenName.textColor = kScreenNameColor;
_mbIcon.hidden = YES;
}else{
_screenName.textColor = kMBScreenNameColor;
_mbIcon.hidden = NO;
_mbIcon.frame = cellFrame.mbIconFrame;
}
// 3. 时间
_time.text = s.create_at;
CGFloat timeX = cellFrame.screenNameFrame.origin.x;
CGFloat timeY = CGRectGetMaxY(cellFrame.screenNameFrame) + kCellBorderWidth;
CGSize timeSize = [_time.text sizeWithAttributes:@{NSFontAttributeName:kTimeFont}];
_time.frame = (CGRect){{timeX, timeY}, timeSize};
// 4. 来源
_source.text = s.source;
CGFloat sourceX = CGRectGetMaxX(_time.frame) + kCellBorderWidth;
CGFloat sourceY = timeY;;
CGSize sourceSize = [_source.text sizeWithAttributes:@{NSFontAttributeName:kSourceFont}];
_source.frame = (CGRect){{sourceX, sourceY}, sourceSize};
// 5. 内容
_statusContent.frame = cellFrame.textFrame;
_statusContent.text = s.statusContent;
// 6. 微博配图
if (s.picUrls.count) {
_images.hidden = NO;
_images.frame = cellFrame.imageFrame;
_images.imageUrlList = s.picUrls;
}else{
_images.hidden = YES;
}
// 7. 被转发的微博
if (s.repostStatus) {
// 父控件是否隐藏
_reposted.hidden = NO;
_reposted.frame = cellFrame.repostedFrame;
// 昵称
_repostedScreenName.frame = cellFrame.repostedScreenNameFrame;
_repostedScreenName.text = [NSString stringWithFormat:@"@%@", s.repostStatus.user.screenName];
// 微博内容
_repostedStatusContent.frame = cellFrame.repostedTextFrame;
_repostedStatusContent.text = s.repostStatus.statusContent;
// 是否有配图
if (s.repostStatus.picUrls.count) {
_repostedImages.hidden = NO;
_repostedImages.frame = cellFrame.repostedImageFrame;
_repostedImages.imageUrlList = s.repostStatus.picUrls;
}else{
_repostedImages.hidden = YES;
}
}else{
_reposted.hidden = YES;
}
// 8. 底部操作条
_cellBar.status = cellFrame.status;
_cellBar.frame = cellFrame.cellBarFrame;
_blankBar.frame = cellFrame.blankBarFrame;
}
#pragma mark 外部微博的点击事件
-(void)statusTap
{
if (_delegate && [_delegate respondsToSelector:@selector(StatusTap:)]) {
[_delegate StatusTap:self];
}
}
#pragma mark 被转发微博的点击事件
-(void)repostStatusTap
{
if (_delegate && [_delegate respondsToSelector:@selector(RepostStatusTap:)]) {
[_delegate RepostStatusTap:self];
}
}
@end
下面看看StatusCell类如何使用:
// 获取最新的微博数据
[StatusTool statusesWithSinceId:sinceId maxId:0 success:^(NSArray *statuses) {
// 1. 拿到最新的微博数据时,计算它的frame
NSMutableArray *newFrames = [NSMutableArray array];
for (Status *s in statuses) {
StatusCellFrame *cellFrame = [[StatusCellFrame alloc]init];
cellFrame.status = s;
[newFrames addObject:cellFrame];
}
// 2. 将newFrames整体插入到旧数据前面
[_statusFrames insertObjects:newFrames atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newFrames.count)]];
// 3. 刷新表格
[self.tableView reloadData];
// 4. 结束刷新
[self.tableView headerEndRefreshing];
// 5. 在顶部展示最新微博的数目
[self showNewStatusCount:(int)statuses.count];
#warning 播放声音不应该在这里
// 播放声音
if (_wavPath) {
NSURL *soundURL = [NSURL fileURLWithPath:_wavPath];
_audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:soundURL error:nil];
[_audioPlayer play];
}
} failure:^(NSError *err) {
// 结束刷新
[self.tableView headerEndRefreshing];
}];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
StatusCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[StatusCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.delegate = self;
}
cell.cellFrame = _statusFrames[indexPath.row];
return cell;
}