Swift - 实现表格tableViewCell里嵌套collectionView(附样例)
(本文代码已升级至Swift4)
有时要实现一个复杂的页面布局,单单使用 UITableView 实现不了,需要通过 UITableView 和 UICollectionView 结合实现,即每个单元格 tableViewCell 中都嵌套一个 collectionView。下面通过样例演示如何实现。
1,效果图
(1)表格中每一个单元格对应一个月份的图书列表。
(2)单元格中头部显示月份标题。内部通过 collectionView 显示当月所有书籍封面图片,数量不定。整个单元格高度自适应。

2,如何实现单元格高度自适应
(1)我们要对 collectionView 设置个高度约束。当在单元格中更新 collectionView 的数据时,要获取这个 collectionView 的真实的内容高度(contentSize.height),然后用 contentSize.height 来更新 collectionView 的高度约束。这样就实现了单元格内部 collectionView 的高度自适应。
(2)而对于单元格 tableViewCell 的高度自适应,是通过 AutoLayout 特性实现的。利用内容将 cell 撑起来。
//设置estimatedRowHeight属性默认值 self.tableView!.estimatedRowHeight = 44.0 //rowHeight属性设置为UITableViewAutomaticDimension self.tableView!.rowHeight = UITableViewAutomaticDimension
3,实现步骤
(1)新建一个自定义的 collectionView 单元格类:MyCollectionViewCell,同时勾选“Also create XIB file”

(2)在 MyCollectionViewCell.xib 中添加一个 ImageView,并设置好约束。同时在对应的类中作关联

(3)MyCollectionViewCell.swift 代码如下:
import UIKit class MyCollectionViewCell: UICollectionViewCell { //用于显示封面缩略图 @IBOutlet weak var imageView: UIImageView! override func awakeFromNib() { super.awakeFromNib() } }
(4)新建一个自定义的 tableView 单元格类:MyTableViewCell,同时勾选“Also create XIB file”

(5)在 MyTableViewCell.xib 中添加一个 Label 和一个 CollectionView,并设置好约束。同时在对应的类中作关联。
其中 Label 设置的是上、下、左、右4个约束:

CollectionView 设置的是左、右、下以及高度这个4个约束:

同时调整下 CollectionView 的 Cell Size 和 Min Spacing:

(6)MyTableViewCell.swift 代码如下:
(7)在 StoryBoard 主视图中添加一个 TableView,并设置好约束。同时在对应的类中作关联。
import UIKit class MyTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource { //单元格标题 @IBOutlet weak var titleLabel: UILabel! //封面图片集合列表 @IBOutlet weak var collectionView: UICollectionView! //collectionView的高度约束 @IBOutlet weak var collectionViewHeight: NSLayoutConstraint! //封面数据 var images:[String] = [] override func awakeFromNib() { super.awakeFromNib() //设置collectionView的代理 self.collectionView.delegate = self self.collectionView.dataSource = self // 注册CollectionViewCell self.collectionView!.register(UINib(nibName:"MyCollectionViewCell", bundle:nil), forCellWithReuseIdentifier: "myCell") } //加载数据 func reloadData(title:String, images:[String]) { //设置标题 self.titleLabel.text = title //保存图片数据 self.images = images //collectionView重新加载数据 self.collectionView.reloadData() //更新collectionView的高度约束 let contentSize = self.collectionView.collectionViewLayout.collectionViewContentSize collectionViewHeight.constant = contentSize.height self.collectionView.collectionViewLayout.invalidateLayout() } //返回collectionView的单元格数量 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return images.count } //返回对应的单元格 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as! MyCollectionViewCell cell.imageView.image = UIImage(named: images[indexPath.item]) return cell } //绘制单元格底部横线 override func draw(_ rect: CGRect) { //线宽 let lineWidth = 1 / UIScreen.main.scale //线偏移量 let lineAdjustOffset = 1 / UIScreen.main.scale / 2 //线条颜色 let lineColor = UIColor(red: 0xe0/255, green: 0xe0/255, blue: 0xe0/255, alpha: 1) //获取绘图上下文 guard let context = UIGraphicsGetCurrentContext() else { return } //创建一个矩形,它的所有边都内缩固定的偏移量 let drawingRect = self.bounds.insetBy(dx: lineAdjustOffset, dy: lineAdjustOffset) //创建并设置路径 let path = CGMutablePath() path.move(to: CGPoint(x: drawingRect.minX, y: drawingRect.maxY)) path.addLine(to: CGPoint(x: drawingRect.maxX, y: drawingRect.maxY)) //添加路径到图形上下文 context.addPath(path) //设置笔触颜色 context.setStrokeColor(lineColor.cgColor) //设置笔触宽度 context.setLineWidth(lineWidth) //绘制路径 context.strokePath() } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } }

(8)ViewController.swift 代码如下:
hangge_1591.zip
import UIKit //每月书籍 struct BookPreview { var title:String var images:[String] } class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { //所有书籍数据 let books = [ BookPreview(title: "五月新书", images: ["0.jpg", "1.jpg","2.jpg", "3.jpg", "4.jpg","5.jpg","6.jpg"]), BookPreview(title: "六月新书", images: ["7.jpg", "8.jpg", "9.jpg"]), BookPreview(title: "七月新书", images: ["10.jpg", "11.jpg", "12.jpg", "13.jpg"]) ] //显示内容的tableView @IBOutlet weak var tableView: UITableView! override func loadView() { super.loadView() } override func viewDidLoad() { super.viewDidLoad() //设置tableView代理 self.tableView!.delegate = self self.tableView!.dataSource = self //去除单元格分隔线 self.tableView!.separatorStyle = .none //创建一个重用的单元格 self.tableView!.register(UINib(nibName:"MyTableViewCell", bundle:nil), forCellReuseIdentifier:"myCell") //设置estimatedRowHeight属性默认值 self.tableView!.estimatedRowHeight = 44.0 //rowHeight属性设置为UITableViewAutomaticDimension self.tableView!.rowHeight = UITableViewAutomaticDimension } //在本例中,只有一个分区 func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1; } //返回表格行数 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.books.count } //创建各单元显示内容(创建参数indexPath指定的单元) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! MyTableViewCell //下面这两个语句一定要添加,否则第一屏显示的collection view尺寸,以及里面的单元格位置会不正确 cell.frame = tableView.bounds cell.layoutIfNeeded() //重新加载单元格数据 cell.reloadData(title:books[indexPath.row].title, images: books[indexPath.row].images) return cell } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }源码下载:

功能修改:每个单元格只显示一行封面图片
上面的样例中,每个单元格内的图片是全部显示出来,有多少显示多少,高度自适应。
我们还可以换种展示方法,每个单元格,即每个 collectionView 只显示一行数据,图片如果多的话可以通过左右滑动查看。
1,效果图


2,实现原理
我们只需要把 collectionView 的滚动方向改成水平方向即可。

如果不需要显示横向的滚动条,可以去掉“Shows Horizontal Indicator”的勾选。

我發現
多條的情況下
將第一個一到最後一張圖
會有問題
非常不错,收藏了
能实现一行cell中多行collectionCell,同是多的部分类似一行左右滑动显示吗?
首先 collectionViewHeight.constant = contentSize.height 会报错,后来我修正了collectionViewHeight = NSLayoutConstraint(item: self.collectionView, attribute: NSLayoutAttribute.height, relatedBy:NSLayoutRelation.equal, toItem:nil, attribute:NSLayoutAttribute.notAnAttribute, multiplier:0.0, constant:400),运行没有报错,但是cell的大小并没有自动适应,collectionView 区域有滚动条
我想单点图片能打开一本书,但是自定义单元格不能用 navigation,尝试用代理,但不知道在哪里实例化单元格类,站长能解答一下吗
如何实现imagview点击事件?点击一下打开对应的书
博主,你有oc版的吗?
站长,我发现一个问题,这种嵌套方式下,如果选中collection中的一个cell想要触发跳转的话,只能用拖拽的方式,没法用performseguewithidentifier的方式,因为好像performsegue是controller才有的方法,但tableviewcell不能继承controller。我现在遇到的情况是一个cell可能根据不同的情况跳转到不同页面,所以拖拽肯定是不行了,一个cell不能拖拽到2个页面,但performseguewithidentifier又用不了,想问下站长有没有什么好的解决办法,谢谢!
航歌大大你好,我很喜歡你的教學,你的文章帶給我很多幫助,非常感謝!
這邊我有個問題,如果我要在tableviewcell中,運用展開cell,裡面再嵌套一個tableview,形成點擊cell,會再展開數個cell,請問我該如實作做呢?
這邊我的構思如下:我有兩個tableviewA跟B,A的cell包裹著B,所以我在storyboard中拉了一個tableviewA,然後新增一個xib,裡面放了另一個tableviewB,並且在裡面的datasourse中return 3個高度總共120的cell,之後在viewcontroller中A register 新增的xib(uitableviewcell),之後就是參考你的另一篇文章,“实现点击UITableView单元格时自动展开单元格”,但結果就是點擊cell毫無反應,我看報錯是說tableviewcell constrain的問題,我該如何解決呢?
你好,我想问下,为什么我用同样的办法创建的cell嵌套collectionView,但是横向滚动显示的时候就有问题呀?我找不到是什么原因,弄这个的时候需要注意什么吗?