有时要实现一个复杂的页面布局,单单使用 UITableView 实现不了,需要通过 UITableView 和 UICollectionView 结合实现,即每个单元格 tableViewCell 中都嵌套一个 collectionView。下面通过样例演示如何实现。
1,效果图
(1)表格中每一个单元格对应一个月份的图书列表。
(2)单元格中头部显示月份标题。内部通过 collectionView 显示当月所有书籍封面图片,数量不定。整个单元格高度自适应。
2,如何实现单元格高度自适应
(1)我们要对 collectionView 设置个高度约束。当在单元格中更新 collectionView 的数据时,要获取这个 collectionView 的真实的内容高度(contentSize.height),然后用 contentSize.height 来更新 collectionView 的高度约束。这样就实现了单元格内部 collectionView 的高度自适应。
(2)而对于单元格 tableViewCell 的高度自适应,是通过 AutoLayout 特性实现的。利用内容将 cell 撑起来。
|
1
2
3
4
|
//设置estimatedRowHeight属性默认值self.tableView!.estimatedRowHeight = 44.0
//rowHeight属性设置为UITableViewAutomaticDimensionself.tableView!.rowHeight = UITableViewAutomaticDimension
|
3,实现步骤
(1)新建一个自定义的 collectionView 单元格类:MyCollectionViewCell,同时勾选“Also create XIB file”
(2)在 MyCollectionViewCell.xib 中添加一个 ImageView,并设置好约束。同时在对应的类中作关联
(3)MyCollectionViewCell.swift 代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
|
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,并设置好约束。同时在对应的类中作关联。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
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”的勾选。
原文链接:http://www.hangge.com/blog/cache/detail_1591.html