自定義封裝UITableView,更加簡潔高效,無需為了實現delegate增加膠水代碼,自帶下拉刷新上拉載入控制項 "項目倉庫地址" 歡迎互相交流學習,問題交流群群號:296406818 如何開始 "項目技術特點" "安裝方法" "框架用法" 代碼結構 "GYTableBaseView.h" "GY ...
自定義封裝UITableView,更加簡潔高效,無需為了實現delegate增加膠水代碼,自帶下拉刷新上拉載入控制項
項目倉庫地址
歡迎互相交流學習,問題交流群群號:296406818
- 如何開始
- 代碼結構
- 使用示例
技術特點
- Section和Cell層次更加清晰,根據傳入的Section數據結構內部已經全部實現Section和Cell相關delegate方法
- Cell實例可獲得外部動態數據,索引位置,上下關係,選中狀態等,隨時更換樣式
- Controller自帶MJRefresh框架,提供下拉刷新和上拉載入功能,外部暴露介面調用
- 提供Cell,Section間距設置,提供選中行高亮、選中行自動居中,提供設置Cell動態高度設置等API
- 框架中的元素全部繼承於原生的tableView相關元素,除部分代理方法外,其他原生方法扔然可以使用
安裝方法
- pod安裝: pod 'GYTableViewController'
- 手動安裝:手動安裝需要添加兩個庫,將GYTableViewController項目文件中Framework文件下的文件導入自身項目,同時此框架基於MJRefresh,所以也需要導入MJRefresh框架文件,手動或者pod都可以,MJRefresh安裝方法
框架用法
請使用該框架中的元素來代替原生列表控制項,對應關係如下:
GYTableBaseView -> UITableView
GYTableViewController -> UITableViewController
GYTableViewCell -> UITableViewCell
GYTableViewSection 原生使用UIView展示section內容,這裡使用GYTableViewSection
SectionVo 用來設置Section樣式與GYTableViewSection實例綁定
CellVo 用來設置Cell樣式與GYTableViewCell實例綁定
使用時有列表控制項的界面直接繼承GYTableViewController,.h示例如下
#import "GYTableViewController.h"
@interface NormalTableViewController : GYTableViewController
.m文件中重寫headerRefresh添加元素,當自帶的下拉刷新控制項下拉時調用;從而開始列表內容層次搭建,以及各種類型的Cell位置如何擺放等
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
//下拉刷新後開始請求後臺提供數據,請求到數據後根據解析的內容展開cell實例和位置等操作,代碼結構如下(偽代碼)
request{
tableView{
sectionVo{
cellVo,
cellVo,
...
}
sectionVo{
cellVo,
...
}
...
}
endRefreshHandler();//界面搭建完畢後停止刷新
}
}
Cell控制項直接繼承GYTableViewCell,.h示例如下
#import "GYTableViewCell.h"
@interface NormalViewCell : GYTableViewCell
.m文件中重寫showSubviews方法進行佈局,利用GET_CELL_DATA或GET_CELL_ARRAY_DATA巨集來獲取列表控制項中傳入的數據,如傳入非數組型的數據使用GET_CELL_DATA,反之使用GET_CELL_ARRAY_DATA
-(void)showSubviews{
GET_CELL_DATA([xxx class]);//先獲取外部傳入的數據
GET_CELL_ARRAY_DATA([xxx class]);//或者獲取數組類型的數據
//開始界面佈局...
}
GYTableBaseView.h
@interface GYTableBaseView : UITableView
/** 設置選中位置 **/
@property(nonatomic,retain) NSIndexPath* selectedIndexPath;
/** 點中cell高亮 **/
@property (nonatomic,assign) BOOL clickCellHighlight;
/** 點中cell自動居中 **/
@property (nonatomic,assign) BOOL clickCellMoveToCenter;
/** 每一節間距 **/
@property (nonatomic,assign) CGFloat sectionGap;
/** 每個cell間距 **/
@property (nonatomic,assign) CGFloat cellGap;
/** 首次下拉 **/
@property (nonatomic,assign,readonly) BOOL hasFirstRefreshed;
/** 下拉刷新 **/
-(void)headerBeginRefresh;
/** 檢查數據間距 **/
-(void)checkGaps;
/** 清除所有數據 **/
-(void)clearAllSectionVo;
/** 添加一節內容 **/
-(void)addSectionVo:(SectionVo*)sectionVo;
/** 在某個索引插入一節內容 **/
-(void)insertSectionVo:(SectionVo*)sectionVo atIndex:(NSInteger)index;
/** 刪除一節內容 **/
-(void)removeSectionVoAt:(NSInteger)index;
/** 重新刷新全部界面 類似源生的reloadData **/
-(void)reloadGYData;
/** 將選中的數據項平滑居中移動 **/
-(void)moveSelectedIndexPathToCenter;
/** 獲取最後一個SectionVo **/
-(SectionVo*)getLastSectionVo;
/** 獲取第一個SectionVo **/
-(SectionVo*)getFirstSectionVo;
/** 獲取目標索引位置SectionVo **/
-(SectionVo*)getSectionVoByIndex:(NSInteger)index;
/** 獲取目標indexPath位置CellVo **/
-(CellVo*)getCellVoByIndexPath:(NSIndexPath*)indexPath;
/** 獲取SectionVo總數 **/
-(NSUInteger)getSectionVoCount;
/** 獲取CellVo總數 所有SectionVo包含的CellVo總和 **/
-(NSUInteger)getTotalCellVoCount;
@end
SectionVo
@interface SectionVo : NSObject
/** 創建SectionVo實例並初始化設置下一步回調 **/
+ (instancetype)initWithParams:(void(^)(SectionVo* svo))nextBlock;
/** 創建SectionVo實例並初始化設置section頁眉高度、section頁眉類型、section頁眉數據、下一步回調 **/
+ (instancetype)initWithParams:(CGFloat)sectionHeaderHeight sectionHeaderClass:(Class)sectionHeaderClass sectionHeaderData:(id)sectionHeaderData nextBlock:(void(^)(SectionVo* svo))nextBlock;
/** 創建SectionVo實例並初始化設置section頁眉高度、section頁眉類型、section頁眉數據、section頁腳高度、section頁腳類型、section頁腳數據、下一步回調 **/
+ (instancetype)initWithParams:(CGFloat)sectionHeaderHeight sectionHeaderClass:(Class)sectionHeaderClass sectionHeaderData:(id)sectionHeaderData sectionFooterHeight:(CGFloat)sectionFooterHeight sectionFooterClass:(Class)sectionFooterClass sectionFooterData:(id)sectionFooterData nextBlock:(void(^)(SectionVo* svo))nextBlock;
/** GYTableViewSection頁眉實例高度 **/
@property (nonatomic,assign)CGFloat sectionHeaderHeight;
/** 用來實例化GYTableViewSection頁眉的自定義類型 **/
@property (nonatomic,retain)Class sectionHeaderClass;
/** 傳遞給GYTableViewSection頁眉實例的數據,用來展示界面判斷邏輯等,實例內部通過self.data屬性獲得 **/
@property (nonatomic,retain)id sectionHeaderData;
/** GYTableViewSection頁腳實例高度 **/
@property (nonatomic,assign)CGFloat sectionFooterHeight;
/** 用來實例化GYTableViewSection頁腳的自定義類型 **/
@property (nonatomic,retain)Class sectionFooterClass;
/** 傳遞給GYTableViewSection頁腳實例的數據,用來展示界面判斷邏輯等,實例內部通過self.data屬性獲得 **/
@property (nonatomic,retain)id sectionFooterData;
/** 該節包含的CellVo個數 **/
-(NSInteger)getCellVoCount;
/** 添加單個CellVo **/
-(void)addCellVo:(CellVo*)cellVo;
/** 批量添加CellVo **/
-(void)addCellVoByList:(NSArray<CellVo*>*)otherVoList;
@end
CellVo
@interface CellVo : NSObject
/** 創建CellVo實例並初始化設置cell高度、cell類型、cell數據 **/
+ (instancetype)initWithParams:(CGFloat)cellHeight cellClass:(Class)cellClass cellData:(id)cellData;
/** 創建CellVo實例並初始化設置cell高度、cell類型、cell數據、cell是否唯一 **/
+ (instancetype)initWithParams:(CGFloat)cellHeight cellClass:(Class)cellClass cellData:(id)cellData isUnique:(BOOL)isUnique;
/** 創建CellVo實例並初始化設置cell高度、cell類型、cell數據、cell是否唯一、是否強制刷新 **/
+ (instancetype)initWithParams:(CGFloat)cellHeight cellClass:(Class)cellClass cellData:(id)cellData isUnique:(BOOL)isUnique forceUpdate:(BOOL)forceUpdate;
/** 通過原始數據數組批量創建CellVo並將數據分別對應存入 **/
+(NSArray<CellVo*>*)dividingCellVoBySourceArray:(CGFloat)cellHeight cellClass:(Class)cellClass sourceArray:(NSArray*)sourceArray;
/** GYTableViewCell實例高度 **/
@property (nonatomic,assign)CGFloat cellHeight;
/** 用來實例化GYTableViewCell的自定義類型 **/
@property (nonatomic,retain)Class cellClass;
/** 傳遞給GYTableViewCell實例的數據,用來展示界面判斷邏輯等,實例內部通過self.data屬性獲得 **/
@property (nonatomic,retain)id cellData;
/** 創建GYTableViewCell實例唯一,相當於單例,就算是相同類型的GYTableViewCell也是各自成為單例,且內容不會刷新(適合內容只初始化一次的界面) **/
@property (nonatomic,assign)BOOL isUnique;
/** isUnique設置為true的情況下 forceUpdate可以繼續讓單例GYTableViewCell中的內容強制刷新 **/
@property (nonatomic,assign)BOOL forceUpdate;
/** 自定義保留欄位 **/
@property (nonatomic,copy)NSString* cellName;
@end
GYTableViewController.h
@interface GYTableViewController : UIViewController<GYTableBaseViewDelegate>
/** controller包含的tableView實例 **/
@property(nonatomic,retain)GYTableBaseView* tableView;
/** 設置進入該頁面是否自動下拉刷新 預設true **/
@property(nonatomic,assign)BOOL autoRefreshHeader;
/** 設置選中某個indexPath位置 **/
@property(nonatomic,retain)NSIndexPath* selectedIndexPath;
/** 設置是否標記重用cell實例 預設true **/
@property(nonatomic,assign)BOOL useCellIdentifer;
/** 設置每次進入該頁面自動滾動到列表頂部 預設false **/
@property(nonatomic,assign)BOOL autoRestOffset;
/** 設置是否顯示下拉刷新控制項 預設true **/
@property(nonatomic,assign)BOOL isShowHeader;
/** 設置是否顯示上拉載入控制項 預設false **/
@property(nonatomic,assign)BOOL isShowFooter;
/** 設置TableView的佈局位置,預設鋪滿Controller **/
-(CGRect)getTableViewFrame;
/** 設置自定義下拉刷新控制項實例 **/
-(MJRefreshHeader*)getRefreshHeader;
/** 設置自定義上拉載入控制項實例 **/
-(MJRefreshFooter*)getRefreshFooter;
@end
GYTableBaseViewDelegate
/** 下拉刷新或上拉載入調用結束Block hasData標註界面是否刷新出了新的數據 **/
typedef void(^HeaderRefreshHandler)(BOOL hasData);
typedef void(^FooterLoadMoreHandler)(BOOL hasData);
@protocol GYTableBaseViewDelegate<UITableViewDelegate,UITableViewDataSource>
@optional
/** 當GYTableBaseView下拉刷新時代理調用 **/
-(void)headerRefresh:(GYTableBaseView*)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler;
@optional
/** 當GYTableBaseView上拉載入時代理調用 **/
-(void)footerLoadMore:(GYTableBaseView*)tableView endLoadMoreHandler:(FooterLoadMoreHandler)endLoadMoreHandler lastSectionVo:(SectionVo*)lastSectionVo;
@optional
/** 當GYTableBaseView下拉刷新完畢後代理調用 **/
-(void)didRefreshComplete:(GYTableBaseView*)tableView;
@optional
/** 當GYTableBaseView上拉載入完畢後代理調用 **/
-(void)didLoadMoreComplete:(GYTableBaseView*)tableView;
@optional
/** 當GYTableBaseView某一條GYTableViewCell實例被點擊時代理調用 **/
-(void)tableView:(GYTableBaseView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
@optional
/** 當GYTableBaseView滾動到某個位置時代理調用 **/
-(void)didScrollToRow:(GYTableBaseView*)tableView indexPath:(NSIndexPath *)indexPath;
@optional
/** 當GYTableBaseView從滾動狀態靜止時代理調用 **/
-(void)didEndScrollingAnimation:(GYTableBaseView*)tableView;
@end
GYTableViewCell.h
@interface GYTableViewCell : UITableViewCell
/** 是否在該節中的首位 **/
@property(nonatomic,assign) BOOL isFirst;
/** 是否在該節中的末位 **/
@property(nonatomic,assign) BOOL isLast;
/** 是否在該節只有此一個GYTableViewCell實例 **/
@property(nonatomic,assign) BOOL isSingle;
/** 是否需要刷新界面 **/
@property(nonatomic,assign) BOOL needRefresh;
/** 是否初次界面載入完畢 **/
@property(nonatomic,assign) BOOL isSubviewShow;
/** 對應的GYTableBaseView實例 **/
@property(nonatomic,weak) GYTableBaseView* tableView;
/** 當前所處位置 **/
@property(nonatomic,retain) NSIndexPath* indexPath;
/** 對應的CellVo實例 **/
@property(nonatomic,retain) CellVo *cellVo;
/** 頁面元素創建並佈局 請勿使用layoutSubviews來佈局 **/
-(void)showSubviews;
/** 是否顯示選中的效果樣式 預設false **/
-(BOOL)showSelectionStyle;
/** 根據內容動態計算高度(適合內容多少高度不定的樣式或文案展示) **/
-(CGFloat)getCellHeight:(CGFloat)cellWidth;
/** 檢查cellData的類型是否是目標類型 並返回cellData **/
-(id)checkCellDataClass:(Class)targetClass;
/** 檢查cellData類型為NSArray中的子元素類型是否是目標類型 並返回cellData **/
-(NSArray*)checkCellArrayDataClass:(Class)arrayMemberClass;
@end
GYTableViewSection.h
@interface GYTableViewSection : UIControl
/** tableView所有section總數 **/
@property(nonatomic,assign)NSInteger sectionCount;
/** 當前section索引位置 **/
@property(nonatomic,assign)NSInteger sectionIndex;
/** 外部傳入的數據 用來佈局或交互等(sectionVo.sectionData) **/
@property(nonatomic,retain)id data;
/** 是否在整個tableView的首位 **/
@property(nonatomic,assign)BOOL isFirst;
/** 是否在整個tableView的末位 **/
@property(nonatomic,assign)BOOL isLast;
@end
添加Cell
列表控制器內部實現
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加一個高度為230,類型為BannerViewCell,展示banner圖片列表的Cell
[svo addCellVo:[CellVo initWithParams:230 cellClass:RefreshBannerViewCell.class cellData:self.bannerUrlGroup]];
}]];
endRefreshHandler(YES);//不要忘了結束刷新,否則刷新動畫會停留原地
}
批量添加Cell
列表控制器內部實現
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加一個高度為230,類型為BannerViewCell,展示banner圖片列表的Cell
[svo addCellVo:[CellVo initWithParams:230 cellClass:RefreshBannerViewCell.class cellData:self.bannerUrlGroup]];
}]];
//註意banner和基金產品列表屬於不同區域,應存放到各自section中添加,管理section視圖會比較方便
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加多個高度為80,類型為RefreshFundViewCell,展示基金信息的Cell
[svo addCellVo:[CellVo initWithParams:80 cellClass:RefreshFundViewCell.class cellData:self.fundModels[0]]];
[svo addCellVo:[CellVo initWithParams:80 cellClass:RefreshFundViewCell.class cellData:self.fundModels[1]]];
[svo addCellVo:[CellVo initWithParams:80 cellClass:RefreshFundViewCell.class cellData:self.fundModels[2]]];
//...
}]];
endRefreshHandler(YES);//不要忘了結束刷新,否則刷新動畫會停留原地
}
相同類型的Cell添加可以修改成通過原數組批量添加
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加多個高度為80,類型為RefreshFundViewCell,展示基金信息的Cell
[svo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundModels]];
}]];
添加Section
如果一節內容需要添加section頁眉視圖,只要在sectionVo實例設置sectionHeaderClass即可,同理section頁腳設置sectionFooterClass
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:36 sectionHeaderClass:RefreshFundViewSection.class sectionHeaderData:@"精品專區" nextBlock:^(SectionVo *svo) {
//添加section內的cell...
}]];
endRefreshHandler(YES);
}
分類結構如下
isUnique唯一性
預設所有相同Class的Cell實例都是相互復用,每次下拉刷新或者table設置reloadData,被覆用的Cell實例都會重新觸發刷新調用showSubviews,從而根據傳遞的data展開;然而,一些特殊的Cell不需要復用或只實例化一次,比如標簽按鈕區域的Cell或者banner區域的Cell,每次下拉都是只用這個實例,可以設置為isUnique作為唯一Cell實例優化提高性能
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//添加一個高度為230,類型為BannerViewCell,展示banner圖片列表的Cell
[svo addCellVo:[CellVo initWithParams:230 cellClass:RefreshBannerViewCell.class cellData:self.bannerUrlGroup isUnique:YES]];
//添加一個高度為90,類型為RefreshHotViewCell,展示banner圖片列表的Cell
[svo addCellVo:[CellVo initWithParams:90 cellClass:RefreshHotViewCell.class cellData:self.hotModels isUnique:YES]];
}]];
endRefreshHandler(YES);
}
調用上拉載入
列表控制器內部設置顯示上拉載入控制器
-(BOOL)isShowFooter{
return YES;
}
列表控制器內部重寫footerLoadMore
//endLoadMoreHandler:結束刷新回調block,lastSectionVo:上一節sectionVo數據,即當前列表頁最後一節
-(void)footerLoadMore:(GYTableBaseView *)tableView endLoadMoreHandler:(FooterLoadMoreHandler)endLoadMoreHandler lastSectionVo:(SectionVo *)lastSectionVo{
[lastSectionVo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundNewModels]];//將新增的CellVo實例繼續添加到上一節SectionVo實例中
endLoadMoreHandler(YES);
}
根據需求添加到列表頁最後一節,或者添加到新的一節數據中,並設置添加上限
if([tableView getTotalCellVoCount] > 30){//總共超出30條數據不添加數據
endLoadMoreHandler(NO);//直接結束上拉載入刷新,並顯示"已經全部載入完畢"
return;
}
//根據業務需求的不同,可以繼續添加到上一節sectionVo,也可以添加到新的一節sectionVo中
if([lastSectionVo getCellVoCount] < 15){//上一節少於15條繼續添加到上一節sectionVo
[lastSectionVo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundNewModels]];
}else{//上一節超了 添加到新的一節sectionVo
[tableView addSectionVo:[SectionVo initWithParams:36 sectionHeaderClass:RefreshFundViewSection.class sectionHeaderData:@"推薦專區" nextBlock:^(SectionVo *svo) {
[svo addCellVoByList:[CellVo dividingCellVoBySourceArray:80 cellClass:RefreshFundViewCell.class sourceArray:self.fundNewModels]];
}]];
}
endLoadMoreHandler(YES);//不要忘了結束上拉載入刷新
更改UITableView的frame
列表控制器內部重寫getTableViewFrame
如存在和容器底部對齊的元素,請在此方法對齊底部位置(預設占滿controller邊界);autoLayerout無需重寫此方法,自行設置tableView和其他元素佈局關係
-(CGRect)getTableViewFrame{
self.noticeBack.frame = CGRectMake(0, 0, self.view.width, 30);
self.submitButton.maxY = self.view.height;//底部按鈕對齊容器底部
//返回設置好的tableView位置frame 高度=總高度-公告區高-底部按鈕高
return CGRectMake(0, self.noticeBack.height, self.view.width, self.view.height - self.noticeBack.height - self.submitButton.height);
}
自定義下拉刷新控制項
列表控制器內部重寫getRefreshHeader
-(MJRefreshHeader *)getRefreshHeader{
return [[DiyRotateRefreshHeader alloc]init];
}
偵聽選中的Cell
列表控制器內部實現代理,原生的方法一致
-(void)tableView:(GYTableBaseView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CellVo* cvo = [tableView getCellVoByIndexPath:indexPath];//獲取到綁定的CellVo
XXClass* cellData = cvo.cellData;//獲得cell的原始數據
//根據數據添加業務邏輯...
}
設置cell點擊效果,cell實例內部重寫showSelectionStyle
-(BOOL)showSelectionStyle{
return YES;
}
設置Cell或Section元素間距
列表控制器內部設置tableView屬性cellGap或sectionGap
- (void)viewDidLoad {
self.tableView.sectionGap = 6;//設置每一節區域之間間距
self.tableView.cellGap = 3;//設置每個Cell之間間距(包含每一節區域)
}
設置選中某個位置的Cell
當刷新完成後設置,列表控制器內部設置tableView屬性selectedIndexPath
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
//..添加cell數據
}]];
tableView.selectedIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];//設置選中某個indexPath
endRefreshHandler(YES);
}
Cell實例設置選中效果,重寫setSelected方法,選中樣式請根據需求自行添加
-(void)setSelected:(BOOL)selected{
[super setSelected:selected];
[self checkCellRelate];//自定義選中樣式方法,非框架內部方法,實現如下
}
Cell實例位置關係isFirst,isLast,位於第一個或最後一個和中間段的Cell樣式不同
-(void)checkCellRelate{
if (self.isFirst) {
[self drawFirstStyle:nodeColor];
}else if(self.isLast){
[self drawLastStyle:nodeColor];
}else{
[self drawNormalStyle:nodeColor];
}
}
設置交互點擊某個位置Cell並高亮
- (void)viewDidLoad {
self.tableView.clickCellHighlight = YES;
}
設置點擊Cell自動居中
- (void)viewDidLoad {
self.tableView.clickCellMoveToCenter = YES;
}
Cell自動調整高度
列表控制器內部設置CellVo傳入高度CELL_AUTO_HEIGHT
-(void)headerRefresh:(GYTableBaseView *)tableView endRefreshHandler:(HeaderRefreshHandler)endRefreshHandler{
[tableView addSectionVo:[SectionVo initWithParams:^(SectionVo *svo) {
[svo addCellVoByList:[CellVo dividingCellVoBySourceArray:CELL_AUTO_HEIGHT cellClass:AutoHeightWeiboCell.class sourceArray:self.weiboModels]];
}]];
endRefreshHandler(YES);
}
Cell實例重寫getCellHeight方法獲取動態高度,獲取高度內容會被緩存不會二次計算
-(CGFloat)getCellHeight:(CGFloat)cellWidth{
WeiboModel* weiboModel = GET_CELL_DATA(WeiboModel.class);//獲取Model
NSString* content = weiboModel.content;//獲取動態內容字元串
CGRect contentSize = [content boundingRectWithSize:CGSizeMake(cellWidth - LEFT_PADDING - RIGHT_PADDING, FLT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:SIZE_TEXT_SECONDARY]}
context:nil];//計算給定範圍內最佳尺寸
return TOPIC_AREA_HEIGHT + contentSize.size.height + IMAGE_AREA_HEIGHT + BOTTOM_PADDING * 2;//返回計算後的最終高度
}