這篇筆記主要記錄了完成一個自適應(yīng)高度的TableView的例子箩做。例子來自https://www.raywenderlich.com/87975/dynamic-table-view-cell-height-ios-8-swift,由于原文用的是swift妥畏,與最新版的語法有所不同,我把這個例子用Objective-C改寫了安吁。demo的代碼地址:
https://github.com/shishujuan/ios_study/tree/master/tableview/TableViewDemo.
1 概覽
這個例子主要完成了一個表格視圖的展示醉蚁,其中每個單元格內(nèi)容可能不同,高度也可能不一樣鬼店,需要動態(tài)適應(yīng)网棍。這個demo做的事情就是從一個RSS地址拉取數(shù)據(jù),然后在表格展示妇智。主要知識點有AutoLayout
滥玷,UITableView
以及CocoaPods
的使用氏身。AutoLayout前一篇文章已經(jīng)有分析過,UITableView是本文的重點惑畴,后面會介紹蛋欣。
而CocoaPods新出現(xiàn)的一個東東,它是iOS開發(fā)中一個第三方庫的依賴管理工具如贷,類似于java中的mvn和nodejs中的npm等陷虎。詳細(xì)信息可以參見唐巧大大的文章介紹[用CocoaPods做iOS程序的依賴管理。我這個demo中用到的Podfile內(nèi)容如下杠袱,可以看到這個demo中需要用到AFNetworkActivityLogger
, MediaRSSParser
尚猿,MBProgressHUD
這三個庫。AFNetworkActivityLogger是AFNetworking2.0的一個擴展楣富,是開發(fā)中很常用的第三方庫凿掂,封裝了一些網(wǎng)絡(luò)請求,可以大幅簡化我們的代碼纹蝴。而MediaRSSParser是這個demo才用到庄萎,用于解析RSS文件。MBProgressHUD也是很常用的一個庫骗灶,用于提示進度惨恭,類似好比我們自己手寫的那些ActivityIndicator,當(dāng)然比我們手寫要方便很多耙旦。
我們只需要在工程目錄下運行pod install
脱羡,就會下載好第三方庫了。如果運行命令卡住了的話免都,可以用這個命令pod install --verbose --no-repo-update
.
platform :ios, '8.0'
pod 'AFNetworkActivityLogger', '~> 2.0'
pod 'MediaRSSParser', '~> 1.0'
pod 'MBProgressHUD', '~> 0.9'
2 創(chuàng)建Scene
本demo創(chuàng)建了3個Scene(除去導(dǎo)航欄)锉罐,Storyboard界面如下:
其中Deviant Art為列表頁面,Deviant Article為不帶圖片的詳情頁绕娘,而Deviant Media則為帶圖片的詳情頁脓规。其中:
- Devian Art頂部有一個嵌入的
Navigation Bar
(導(dǎo)航欄,邊上有一個刷新按鈕用于重新加載數(shù)據(jù))险领,下面有一個Text Field
控件的搜索框侨舆,搜索框下面是一個Table View
,用于展示作品列表绢陌。注意挨下,我們這里還沒有添加Table View Cell
,稍后我們會看到怎么自定義這個Cell脐湾。 - Devian Article為不含圖片的作品詳情頁臭笆。這個場景只有兩個Label,分別是標(biāo)題和副標(biāo)題。
- Devian Media為帶圖片的作品詳情頁愁铺。這個場景除了標(biāo)題和副標(biāo)題兩個Label外鹰霍,還有一個Image View。
3 解析RSS
解析RSS借助了第三方庫RSSParser來實現(xiàn)茵乱。代碼如下:
(void)parseForQuery:(NSString *)query {
[self showProgressHUD];
RSSParser *parser = [[RSSParser alloc] init];
[parser parseRSSFeed:deviantArtBaseStringUrlString parameters:[self parametersForQuery:query] success:^(RSSChannel *channel) {
[self convertItemPropertiesToPlainText:channel.items];
self.items = channel.items;
[self hideProgressHUD];
[self reloadTableViewContent];
} failure:^(NSError *error) {
[self hideProgressHUD];
NSLog(@"Error:%@", error);
}];
}
- (void)showProgressHUD {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:true];
hud.labelText = @"Loading";
}
- (void)hideProgressHUD {
[MBProgressHUD hideHUDForView:self.view animated:true];
}
其中showProgressHUD和hideProgressHUD函數(shù)用到了第三方庫MBProgressHUD來實現(xiàn)茂洒,省去了我們自己去添加Activity Indicator
。解析RSS獲取到了作品列表似将,我們需要展示在Table View中获黔,因此接下來我們就要添加自定義的Table View Cell。
4 創(chuàng)建Basic Cell
添加一個Table View Cell到Table View中在验,并在屬性標(biāo)簽中設(shè)置Class為BasicCell玷氏,設(shè)置初始的rowHeight為83。其中內(nèi)容為兩個Label(Title Label字體大小為17腋舌,Subtitle Label為15盏触,包含圖片的Cell我們在后面再添加),如下所示:
約束關(guān)系如下所示:
然后設(shè)置兩個Label的抗壓縮和抗拉伸優(yōu)先級块饺。這里設(shè)置Title Label的抗壓縮和抗拉伸優(yōu)先級為751赞辩,高于Subtitle Label的750,也就是說要優(yōu)先滿足Title Label的約束:
5 配置Table View
為了動態(tài)設(shè)置Cell的高度授艰,先要配置下Table View的幾個屬性辨嗽。如下所示,設(shè)置了估算高度以及rowHeight屬性淮腾,此外糟需,設(shè)置Table View的delegate和dataSource為View Controller自身。注意谷朝,需要設(shè)置View Controller的automaticallyAdjustsScrollViewInsets為NO洲押,否則會看到Table View與Text Field之間有一段空白。
- (void)configureTableView {
//設(shè)置rowHeight為AutomaticDimension是為了讓Table View通過Auto Layout的約束去定義每個Cell的高度圆凰。
self.feedTableView.rowHeight = UITableViewAutomaticDimension;
//設(shè)置estimatedRowHeight為了提高Table View的渲染效率杈帐,這是一個估算高度。
self.feedTableView.estimatedRowHeight = 160.0;
self.feedTableView.delegate = self;
self.feedTableView.dataSource = self;
self.searchTextField.delegate = self;
//注意专钉,不設(shè)置這個會導(dǎo)致Table View與searchTextField有一段空白挑童。
self.automaticallyAdjustsScrollViewInsets = NO;
}
接下來實現(xiàn)dataSource和deletegate的方法。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self basicCellAtIndexPath:indexPath];
}
- (BasicCell *)basicCellAtIndexPath:(NSIndexPath *)indexPath {
BasicCell *cell = [self.feedTableView dequeueReusableCellWithIdentifier:basicCellIdentifier];
[self setTitleForCell:cell indexPath:indexPath];
[self setSubtitleForCell:cell indexPath:indexPath];
return cell;
}
- (void)setTitleForCell:(BasicCell *)cell indexPath:(NSIndexPath *)indexPath {
RSSItem *item = self.items[indexPath.row];
cell.titleLabel.text = item.title ? item.title : @"[No Title]";
}
- (void)setSubtitleForCell:(BasicCell *)cell indexPath:(NSIndexPath *)indexPath {
RSSItem *item = self.items[indexPath.row];
NSString *subTitleText = item.mediaText ? item.mediaText : item.mediaDescription;
if (subTitleText) {
subTitleText = subTitleText.length > 200 ? [subTitleText substringToIndex:200] : subTitleText;
} else {
subTitleText = @"";
}
cell.subtitleLabel.text = subTitleText;
}
6 添加Image Cell
為了顯示圖片跃须,需要新增加一個Table View Cell炮沐,這里設(shè)置標(biāo)識為Image Cell
。與Basic Cell不同的是回怜,需要增加一個Image View用來顯示圖片。添加的約束如下所示,具體參見例子代碼:
這里有幾個地方要注意一下:
- Image View的寬度和高度都設(shè)置的100pt玉雾,注意翔试,這兩個約束的優(yōu)先級這里設(shè)置為999。此外复旬,Image View與Cell底部的距離>=20這個約束的優(yōu)先級也設(shè)置為999垦缅。
- Subtitle Label與Cell底部的距離 >= 20 這個約束的優(yōu)先級為1000。也就是說要優(yōu)先滿足Subtitle Label的約束驹碍。
接下來需要設(shè)置Image Cell壁涎,相關(guān)代碼如下:
//修改cellForRowAtIndexPath方法,區(qū)分有圖片還是沒圖片分開處理志秃。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([self hasImageAtIndexPath:indexPath]) {
return [self imageCellAtIndexPath:indexPath];
} else {
return [self basicCellAtIndexPath:indexPath];
}
}
- (BOOL)hasImageAtIndexPath:(NSIndexPath *)indexPath {
RSSItem *item = self.items[indexPath.row];
NSArray<RSSMediaThumbnail *> *mediaThumbnailArray = [item mediaThumbnails];
for (RSSMediaThumbnail *mediaThumbnail in mediaThumbnailArray) {
if (mediaThumbnail.url != nil) {
return YES;
}
}
return NO;
}
- (ImageCell *)imageCellAtIndexPath:(NSIndexPath *)indexPath {
//在Storyboard中已經(jīng)設(shè)置了ImageCell標(biāo)識怔球,
//所以這里是肯定可以取得cell的,可以省去是否為nil的判斷浮还。
ImageCell *cell = [self.feedTableView dequeueReusableCellWithIdentifier:imageCellIdentifier];
[self setImageForCell:cell indexPath:indexPath];
[self setTitleForCell:cell indexPath:indexPath];
[self setSubtitleForCell:cell indexPath:indexPath];
return cell;
}
- (void)setImageForCell:(ImageCell *)cell indexPath:(NSIndexPath *)indexPath {
RSSItem *item = self.items[indexPath.row];
RSSMediaThumbnail *mediaThumbnail;
if (item.mediaThumbnails.count >= 2) {
mediaThumbnail = item.mediaThumbnails[1];
} else {
mediaThumbnail = item.mediaThumbnails[0];
}
//預(yù)設(shè)圖片為nil竟坛,防止之前的圖片重用導(dǎo)致看起來圖片錯亂
cell.customImageView.image = nil;
if (mediaThumbnail.url != nil) {
[cell.customImageView setImageWithURL:mediaThumbnail.url];
}
}
7 其他
另外需要設(shè)置Basic Cell和Image Cell到對應(yīng)作品詳情視圖的segue,這個具體參見代碼钧舌。另外担汤,對Table View的Cell高度的估算也在最終代碼有優(yōu)化。最終運行效果如下圖所示: