美文网首页swift编程开发iOS学习开发iOS进阶指南
iOS学习笔记(6)-自适应高度的Table View

iOS学习笔记(6)-自适应高度的Table View

作者: __七把刀__ | 来源:发表于2016-05-31 23:08 被阅读2598次

这篇笔记主要记录了完成一个自适应高度的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 概览

这个例子主要完成了一个表格视图的展示,其中每个单元格内容可能不同,高度也可能不一样,需要动态适应。这个demo做的事情就是从一个RSS地址拉取数据,然后在表格展示。主要知识点有AutoLayoutUITableView以及CocoaPods的使用。AutoLayout前一篇文章已经有分析过,UITableView是本文的重点,后面会介绍。

而CocoaPods新出现的一个东东,它是iOS开发中一个第三方库的依赖管理工具,类似于java中的mvn和nodejs中的npm等。详细信息可以参见唐巧大大的文章介绍[用CocoaPods做iOS程序的依赖管理。我这个demo中用到的Podfile内容如下,可以看到这个demo中需要用到AFNetworkActivityLogger, MediaRSSParserMBProgressHUD这三个库。AFNetworkActivityLogger是AFNetworking2.0的一个扩展,是开发中很常用的第三方库,封装了一些网络请求,可以大幅简化我们的代码。而MediaRSSParser是这个demo才用到,用于解析RSS文件。MBProgressHUD也是很常用的一个库,用于提示进度,类似好比我们自己手写的那些ActivityIndicator,当然比我们手写要方便很多。

我们只需要在工程目录下运行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 创建Scene

本demo创建了3个Scene(除去导航栏),Storyboard界面如下:

图1 初始Storyboard

其中Deviant Art为列表页面,Deviant Article为不带图片的详情页,而Deviant Media则为带图片的详情页。其中:

  • Devian Art顶部有一个嵌入的Navigation Bar(导航栏,边上有一个刷新按钮用于重新加载数据),下面有一个Text Field控件的搜索框,搜索框下面是一个Table View,用于展示作品列表。注意,我们这里还没有添加Table View Cell,稍后我们会看到怎么自定义这个Cell。
  • Devian Article为不含图片的作品详情页。这个场景只有两个Label,分别是标题和副标题。
  • Devian Media为带图片的作品详情页。这个场景除了标题和副标题两个Label外,还有一个Image View。

3 解析RSS

解析RSS借助了第三方库RSSParser来实现。代码如下:

(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函数用到了第三方库MBProgressHUD来实现,省去了我们自己去添加Activity Indicator。解析RSS获取到了作品列表,我们需要展示在Table View中,因此接下来我们就要添加自定义的Table View Cell。

4 创建Basic Cell

添加一个Table View Cell到Table View中,并在属性标签中设置Class为BasicCell,设置初始的rowHeight为83。其中内容为两个Label(Title Label字体大小为17,Subtitle Label为15,包含图片的Cell我们在后面再添加),如下所示:

图2 Basic Cell

约束关系如下所示:

图3 Title Label约束 图4 Subtitle Label约束

然后设置两个Label的抗压缩和抗拉伸优先级。这里设置Title Label的抗压缩和抗拉伸优先级为751,高于Subtitle Label的750,也就是说要优先满足Title Label的约束:

图5 Title Label的抗压缩抗拉伸优先级设置 图6 Subtitle Label的抗压缩抗拉伸优先级设置

5 配置Table View

为了动态设置Cell的高度,先要配置下Table View的几个属性。如下所示,设置了估算高度以及rowHeight属性,此外,设置Table View的delegate和dataSource为View Controller自身。注意,需要设置View Controller的automaticallyAdjustsScrollViewInsets为NO,否则会看到Table View与Text Field之间有一段空白。

- (void)configureTableView {
    //设置rowHeight为AutomaticDimension是为了让Table View通过Auto Layout的约束去定义每个Cell的高度。
    self.feedTableView.rowHeight = UITableViewAutomaticDimension;     
    //设置estimatedRowHeight为了提高Table View的渲染效率,这是一个估算高度。
    self.feedTableView.estimatedRowHeight = 160.0;
    self.feedTableView.delegate = self; 
    self.feedTableView.dataSource = self;
    self.searchTextField.delegate = self;

    //注意,不设置这个会导致Table View与searchTextField有一段空白。
    self.automaticallyAdjustsScrollViewInsets = NO; 
}

接下来实现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,这里设置标识为Image Cell。与Basic Cell不同的是,需要增加一个Image View用来显示图片。添加的约束如下所示,具体参见例子代码:

图7 Image Cell约束

这里有几个地方要注意一下:

  • Image View的宽度和高度都设置的100pt,注意,这两个约束的优先级这里设置为999。此外,Image View与Cell底部的距离>=20这个约束的优先级也设置为999。
  • Subtitle Label与Cell底部的距离 >= 20 这个约束的优先级为1000。也就是说要优先满足Subtitle Label的约束。

接下来需要设置Image Cell,相关代码如下:

//修改cellForRowAtIndexPath方法,区分有图片还是没图片分开处理。
- (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中已经设置了ImageCell标识,
    //所以这里是肯定可以取得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];
    }
    
    //预设图片为nil,防止之前的图片重用导致看起来图片错乱
    cell.customImageView.image = nil;
    
    if (mediaThumbnail.url != nil) {
        [cell.customImageView setImageWithURL:mediaThumbnail.url];
    }
}

7 其他

另外需要设置Basic Cell和Image Cell到对应作品详情视图的segue,这个具体参见代码。另外,对Table View的Cell高度的估算也在最终代码有优化。最终运行效果如下图所示:

图8 运行效果

8 参考资料

相关文章

网友评论

    本文标题:iOS学习笔记(6)-自适应高度的Table View

    本文链接:https://www.haomeiwen.com/subject/bqthdttx.html