使用 UIScrollView 和 UIPageControl 动态填充多个 TableView (Swift 5)
Dynamically populating multiple TableViews using UIScrollView and UIPageControl (Swift 5)
我正在尝试重新创建类似于 iPhone 的本机天气应用程序,您可以在其中 select 多个位置并将它们作为 TableView 附加到带有 UIPageControl[=13 的 UIScrollView =]
我从二维数组中的硬编码模型开始。我希望每个数组都放在每个 tableView 中,总共五个 tableView,第一个 tableView 中有一行,第二个中有两行,依此类推:
var model: [[String]] = [
["TableView 1: row 1"],
["TableView 2: row 1", "TableView 2: row 2"],
["TableView 3: row 1", "TableView 3: row 2", "TableView 3: row 3"],
["TableView 4: row 1", "TableView 4: row 2", "TableView 4: row 3", "TableView 4: row 4"],
["TableView 5: row 1", "TableView 5: row 2", "TableView 5: row 3", "TableView 5: row 4", "TableView 5: row 5"]
]
现在我只能围绕一些非常简单的事情,如下所示,所以第一个 tableView 有 5 个单元格,上面写着“TableView 1”,第二个有五个单元格,上面写着“TableView 2” "等等:
//number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
//cell for row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
for i in 0...model.count-1{
//grab the correct tableView from the array of tableViews
if tableView == self.tableViews[i]{
//dequeue the cell using a nib
let cell = tableView.dequeueReusableCell(withIdentifier: ProtoTypeTableViewCell.identifier, for: indexPath) as! ProtoTypeTableViewCell
//set text for the label in the nib file
cell.label.text = "TableView \(i+1)"
return cell
}
}
return UITableViewCell()
}
如有任何帮助,我们将不胜感激;谢谢。
这对我来说是一个非常有趣的问题,因为我确实喜欢重新创建 UI 的流行应用程序。我认为有几种方法可以实现这一点,我将向您展示一种方法。
我认为要解决的主要挑战:
- 用于设置的最佳 UI 组件
- 获取从二维数组映射到右侧 table 视图的正确模型
对于 UI 个组件,我选择了这种方式:
- 分页UI滚动视图
- UIStackView包含在Paging中 UIScrollView
- UITableView 包含在 UIStackView
以下是我如何着手进行此操作并添加评论来解释
初始设置
class PagingTableView: UIViewController
{
// Same model as yours
var model: [[String]] = [
["TableView 1: row 1"],
["TableView 2: row 1",
"TableView 2: row 2"],
["TableView 3: row 1",
"TableView 3: row 2",
"TableView 3: row 3"],
["TableView 4: row 1",
"TableView 4: row 2",
"TableView 4: row 3",
"TableView 4: row 4"],
["TableView 5: row 1",
"TableView 5: row 2",
"TableView 5: row 3",
"TableView 5: row 4",
"TableView 5: row 5"]
]
let scrollView: UIScrollView =
{
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
let stackView: UIStackView =
{
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
let pageControl = UIPageControl()
let padding: CGFloat = 20
let pageWidth = UIScreen.main.bounds.width
let tableViewCellIdentifier = "cell"
// Use this factor to unique identify your table
let tableViewUniqueIdFactor = 1000
//
// You can ignore this function, created for convenience
private func randomColor() -> UIColor
{
let red = CGFloat(arc4random_uniform(256)) / 255.0
let blue = CGFloat(arc4random_uniform(256)) / 255.0
let green = CGFloat(arc4random_uniform(256)) / 255.0
return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
override func viewDidLoad()
{
super.viewDidLoad()
title = "Paging TableView"
view.backgroundColor = .white
// Configure everything, functions come later
configureScrollViewLayout()
configureStackViewLayout()
configurePageControl()
addTableViewsToStackView()
}
配置滚动视图布局
private func configureScrollViewLayout()
{
scrollView.delegate = self
view.addSubview(scrollView)
// Auto layout
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
.isActive = true
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
constant: padding).isActive = true
scrollView.widthAnchor.constraint(equalToConstant: pageWidth).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -padding * 3).isActive = true
}
配置堆栈视图布局
private func configureStackViewLayout()
{
scrollView.addSubview(stackView)
// Auto layout
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor)
.isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor)
.isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor)
.isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
.isActive = true
stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor,
multiplier: 1).isActive = true
}
配置页面控件布局
private func configurePageControl()
{
pageControl.numberOfPages = model.count
pageControl.currentPage = 0
pageControl.tintColor = randomColor()
pageControl.pageIndicatorTintColor = randomColor()
pageControl.currentPageIndicatorTintColor = randomColor()
view.addSubview(pageControl)
// Auto layout
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
.isActive = true
pageControl.topAnchor.constraint(equalTo: scrollView.bottomAnchor)
.isActive = true
pageControl.widthAnchor.constraint(equalToConstant: 200)
.isActive = true
pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
.isActive = true
}
配置 table 视图并将其添加到堆栈视图
private func addTableViewsToStackView()
{
for modelIndex in 0 ..< model.count
{
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
// Uniquely identify each table which will come in handy
// when figuring out which model should be loaded for a specific
// table view
tableView.tag = tableViewUniqueIdFactor + modelIndex
// Register a default UITableView Cell
tableView.register(UITableViewCell.self,
forCellReuseIdentifier: tableViewCellIdentifier)
tableView.dataSource = self
tableView.backgroundColor = randomColor()
// remove additional rows
tableView.tableFooterView = UIView()
stackView.addArrangedSubview(tableView)
tableView.widthAnchor.constraint(equalToConstant: pageWidth)
.isActive = true
// height is calculated automatically based on the height
// of the stack view
}
}
// end of the class PagingTableView
}
在这个阶段,如果你 运行 这个,你要 运行 这个应用程序,你会看到设置就像没有数据的天气应用程序,分页控件是尚未连线,但我们的页数与模型 (5) 相同:
现在剩下的就是将页面控件和table视图连接到模型
使用滚动视图委托更新页面视图
extension PagingTableView: UIScrollViewDelegate
{
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
{
// Make sure you don't do anything with table view scrolls
// This is only a worry if the view controller is the table view's
// delegate also
if !(scrollView is UITableView)
{
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
}
}
实现 table 视图数据源以将 table 映射到模型
extension PagingTableView: UITableViewDataSource
{
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
// Retrieve the correct model using the unique identifier created earlier
let modelIndex = tableView.tag - tableViewUniqueIdFactor
// Get the correct array needed from model
let modelForeCurrentTable = model[modelIndex]
return modelForeCurrentTable.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellIdentifier)!
// Retrieve the correct model using the unique identifier created earlier
let modelIndex = tableView.tag - tableViewUniqueIdFactor
// Get the correct array needed from model
let modelForeCurrentTable = model[modelIndex]
cell.textLabel?.text = modelForeCurrentTable[indexPath.row]
cell.backgroundColor = .clear
return cell
}
}
现在一切都设置好了,您会得到类似于天气应用程序的东西,每个 table 视图中都会显示正确的数据,并且页面控件也已连接:
最终评论
如果您使用框架或故事板创建 UI,则可以跳过布局部分。我试图从头到尾给你一些你会 运行 孤立的东西,你可以通过调整来玩弄它。
我正在尝试重新创建类似于 iPhone 的本机天气应用程序,您可以在其中 select 多个位置并将它们作为 TableView 附加到带有 UIPageControl[=13 的 UIScrollView =]
我从二维数组中的硬编码模型开始。我希望每个数组都放在每个 tableView 中,总共五个 tableView,第一个 tableView 中有一行,第二个中有两行,依此类推:
var model: [[String]] = [
["TableView 1: row 1"],
["TableView 2: row 1", "TableView 2: row 2"],
["TableView 3: row 1", "TableView 3: row 2", "TableView 3: row 3"],
["TableView 4: row 1", "TableView 4: row 2", "TableView 4: row 3", "TableView 4: row 4"],
["TableView 5: row 1", "TableView 5: row 2", "TableView 5: row 3", "TableView 5: row 4", "TableView 5: row 5"]
]
现在我只能围绕一些非常简单的事情,如下所示,所以第一个 tableView 有 5 个单元格,上面写着“TableView 1”,第二个有五个单元格,上面写着“TableView 2” "等等:
//number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
//cell for row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
for i in 0...model.count-1{
//grab the correct tableView from the array of tableViews
if tableView == self.tableViews[i]{
//dequeue the cell using a nib
let cell = tableView.dequeueReusableCell(withIdentifier: ProtoTypeTableViewCell.identifier, for: indexPath) as! ProtoTypeTableViewCell
//set text for the label in the nib file
cell.label.text = "TableView \(i+1)"
return cell
}
}
return UITableViewCell()
}
如有任何帮助,我们将不胜感激;谢谢。
这对我来说是一个非常有趣的问题,因为我确实喜欢重新创建 UI 的流行应用程序。我认为有几种方法可以实现这一点,我将向您展示一种方法。
我认为要解决的主要挑战:
- 用于设置的最佳 UI 组件
- 获取从二维数组映射到右侧 table 视图的正确模型
对于 UI 个组件,我选择了这种方式:
- 分页UI滚动视图
- UIStackView包含在Paging中 UIScrollView
- UITableView 包含在 UIStackView
- UIStackView包含在Paging中 UIScrollView
以下是我如何着手进行此操作并添加评论来解释
初始设置
class PagingTableView: UIViewController
{
// Same model as yours
var model: [[String]] = [
["TableView 1: row 1"],
["TableView 2: row 1",
"TableView 2: row 2"],
["TableView 3: row 1",
"TableView 3: row 2",
"TableView 3: row 3"],
["TableView 4: row 1",
"TableView 4: row 2",
"TableView 4: row 3",
"TableView 4: row 4"],
["TableView 5: row 1",
"TableView 5: row 2",
"TableView 5: row 3",
"TableView 5: row 4",
"TableView 5: row 5"]
]
let scrollView: UIScrollView =
{
let scrollView = UIScrollView()
scrollView.isPagingEnabled = true
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
let stackView: UIStackView =
{
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
let pageControl = UIPageControl()
let padding: CGFloat = 20
let pageWidth = UIScreen.main.bounds.width
let tableViewCellIdentifier = "cell"
// Use this factor to unique identify your table
let tableViewUniqueIdFactor = 1000
//
// You can ignore this function, created for convenience
private func randomColor() -> UIColor
{
let red = CGFloat(arc4random_uniform(256)) / 255.0
let blue = CGFloat(arc4random_uniform(256)) / 255.0
let green = CGFloat(arc4random_uniform(256)) / 255.0
return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
override func viewDidLoad()
{
super.viewDidLoad()
title = "Paging TableView"
view.backgroundColor = .white
// Configure everything, functions come later
configureScrollViewLayout()
configureStackViewLayout()
configurePageControl()
addTableViewsToStackView()
}
配置滚动视图布局
private func configureScrollViewLayout()
{
scrollView.delegate = self
view.addSubview(scrollView)
// Auto layout
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
.isActive = true
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
constant: padding).isActive = true
scrollView.widthAnchor.constraint(equalToConstant: pageWidth).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -padding * 3).isActive = true
}
配置堆栈视图布局
private func configureStackViewLayout()
{
scrollView.addSubview(stackView)
// Auto layout
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor)
.isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor)
.isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor)
.isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
.isActive = true
stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor,
multiplier: 1).isActive = true
}
配置页面控件布局
private func configurePageControl()
{
pageControl.numberOfPages = model.count
pageControl.currentPage = 0
pageControl.tintColor = randomColor()
pageControl.pageIndicatorTintColor = randomColor()
pageControl.currentPageIndicatorTintColor = randomColor()
view.addSubview(pageControl)
// Auto layout
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
.isActive = true
pageControl.topAnchor.constraint(equalTo: scrollView.bottomAnchor)
.isActive = true
pageControl.widthAnchor.constraint(equalToConstant: 200)
.isActive = true
pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
.isActive = true
}
配置 table 视图并将其添加到堆栈视图
private func addTableViewsToStackView()
{
for modelIndex in 0 ..< model.count
{
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
// Uniquely identify each table which will come in handy
// when figuring out which model should be loaded for a specific
// table view
tableView.tag = tableViewUniqueIdFactor + modelIndex
// Register a default UITableView Cell
tableView.register(UITableViewCell.self,
forCellReuseIdentifier: tableViewCellIdentifier)
tableView.dataSource = self
tableView.backgroundColor = randomColor()
// remove additional rows
tableView.tableFooterView = UIView()
stackView.addArrangedSubview(tableView)
tableView.widthAnchor.constraint(equalToConstant: pageWidth)
.isActive = true
// height is calculated automatically based on the height
// of the stack view
}
}
// end of the class PagingTableView
}
在这个阶段,如果你 运行 这个,你要 运行 这个应用程序,你会看到设置就像没有数据的天气应用程序,分页控件是尚未连线,但我们的页数与模型 (5) 相同:
现在剩下的就是将页面控件和table视图连接到模型
使用滚动视图委托更新页面视图
extension PagingTableView: UIScrollViewDelegate
{
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
{
// Make sure you don't do anything with table view scrolls
// This is only a worry if the view controller is the table view's
// delegate also
if !(scrollView is UITableView)
{
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
}
}
实现 table 视图数据源以将 table 映射到模型
extension PagingTableView: UITableViewDataSource
{
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
// Retrieve the correct model using the unique identifier created earlier
let modelIndex = tableView.tag - tableViewUniqueIdFactor
// Get the correct array needed from model
let modelForeCurrentTable = model[modelIndex]
return modelForeCurrentTable.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellIdentifier)!
// Retrieve the correct model using the unique identifier created earlier
let modelIndex = tableView.tag - tableViewUniqueIdFactor
// Get the correct array needed from model
let modelForeCurrentTable = model[modelIndex]
cell.textLabel?.text = modelForeCurrentTable[indexPath.row]
cell.backgroundColor = .clear
return cell
}
}
现在一切都设置好了,您会得到类似于天气应用程序的东西,每个 table 视图中都会显示正确的数据,并且页面控件也已连接:
最终评论
如果您使用框架或故事板创建 UI,则可以跳过布局部分。我试图从头到尾给你一些你会 运行 孤立的东西,你可以通过调整来玩弄它。