使用 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,则可以跳过布局部分。我试图从头到尾给你一些你会 运行 孤立的东西,你可以通过调整来玩弄它。