如何将tableFooterView始终保持在UITableView的底部 [英] How to keep tableFooterView always on bottom of UITableView

查看:256
本文介绍了如何将tableFooterView始终保持在UITableView的底部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个UITableView,其中的部分数量可变.每个部分都有可变数量的单元格,每个部分都有页眉和页脚.我的UITableView也有一个tableFooterView,我想一直保持在屏幕底部,除非表格太大而无法容纳在屏幕上,则tableFooterView应该显示在最后一节的下方.我要完成的任务在此处说明:

I have a UITableView with a variable amount of sections. Each section has a variable amount of cells and every section has a header and a footer. My UITableView also has a tableFooterView which I want to keep on the bottom of the screen at all times, except when the table is too large to fit on the screen, then the tableFooterView should be shown below the last section. What I want to accomplish is illustrated here:

我想要的示例,方案1

我想要的示例,方案2

但是,当前tableFooterView始终位于最后一部分的正下方,因此,例如,当只有两个部分时,它看起来像这样:

However, currently the tableFooterView is always located right beneath the last section, so when there are for example only two sections, it looks like this:

我目前拥有的示例

我正在寻找一种在每种可能的情况下始终将其保持在底部的方法.我一直在寻找,并且因为Apple不支持tableFooterView的自动版式,所以我还没有找到解决方案.类似的情况在上一节中将tableFooterView替换为sectionFooter,但是我已经不能使用sectionFooters了.

I am looking for a way to keep it always at the bottom, in every possible scenario. I have been looking around and because Apple doesn't support AutoLayout for the tableFooterView, I haven't found a solution yet. Similar cases replace the tableFooterView with a sectionFooter on the last section, but I can't do that as I already have sectionFooters.

有人可以帮助我或为我指明正确的方向吗?需要考虑的几件事:

Is there anybody who can help me out or point me towards the right direction? A couple of things to consider:

  • 它必须是tableFooterView;
  • 用户可以将部分添加到UITableView,并将行添加到这些部分,因此tableFooterView应该随后更新其位置
  • It has to be a tableFooterView;
  • Users can add sections to the UITableView and rows to the sections, so the tableFooterView should then update its location

目前如何设置tableFooterView:

class CustomView: UITableViewDelegate, UITableViewDataSource {

    var myTableFooter: UIView = {

        let myTableFooter = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
        myTableFooter.backgroundColor = .red
        myTableFooter.isUserInteractionEnabled = true
        return myTableFooter

    }()

    override init(frame: CGRect) {

        super.init(frame: frame)
        setupViews()

        MyTableView.tableFooterView = myTableFooter

    }

}

编辑:按照建议尝试了scrollViewDidScroll方法,但没有成功:

Tried the scrollViewDidScroll method as suggested, but didn't work:

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    if(scrollView == myTableView) {

        let neededHeight = myTableView.frame.height - 50 - view.safeAreaInsets.bottom
        let currentHeight = myTableView.contentSize.height - 50

        let heightDifference = neededHeight - currentHeight

        if(heightDifference > 0) {

            myTableView.tableFooterView?.transform = CGAffineTransform(translationX: 0, y: heightDifference)

        }

    }

}

推荐答案

一种方法是:

  • 使用扩展名定义自动调整大小的非滚动"表格视图
  • 将表视图和普通的UIView嵌入容器"视图中的页脚"视图
  • 将容器视图嵌入到滚动视图中,高度与滚动视图相同,但优先级较低
  • 将页脚视图限制在容器视图的底部,并将>=限制在表视图的底部
  • use an extension to define a "self-sizing non-scrolling" table view
  • embed the table view and a normal UIView for the "footer" view in a "container" view
  • embed the container view in a scroll view, with a height equal to the scroll view but with a low priority
  • constrain the footer view to the bottom of the container view, and >= to the bottom of the table view

因此,tableView的自动高度" +页脚视图的高度确定了容器视图的高度,而容器视图的高度决定了滚动视图的.contentSize.页脚视图将粘贴"到容器视图的底部.当滚动视图具有足够的内容时,它将按下"页脚视图.

So, the "auto-height" of the tableView + the height of the footer view determines the height of the container view, which determines the .contentSize of the scroll view. The footer view will "stick" to the bottom of the container view. When the scroll view has enough content, it will "push down" the footer view.

示例:

这里是创建代码的代码.一切都通过代码完成...不需要IBOutlets,因此只需创建一个新的视图控制器并将其类分配给PennyWiseViewController:

Here is the code to create that. Everything is done via code... no IBOutlets needed, so just create a new view controller and assign its class to PennyWiseViewController:

//
//  PennyWiseViewController.swift
//
//  Created by Don Mag on 5/14/19.
//

import UIKit

final class ContentSizedTableView: UITableView {

    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }

}

class MyOneLabelCell: UITableViewCell {

    // very simple one-label tableView cell

    let theLabel: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.numberOfLines = 0
        return v
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        contentView.addSubview(theLabel)

        NSLayoutConstraint.activate([
            theLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8.0),
            theLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8.0),
            theLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0),
            theLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8.0),
            ])

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

class PennyWiseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    let theContainerView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    let theScrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    let theTableView: ContentSizedTableView = {
        let v = ContentSizedTableView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.isScrollEnabled = false
        return v
    }()

    let theFooterView: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .red
        v.textColor = .white
        v.text = "The Footer View"
        v.textAlignment = .center
        return v
    }()

    // start with 3 sections
    // selecting the row in the first section allows adding sections
    // selecting the row in the second section allows deleting sections
    var numSections = 3

    let reuseID = "MyOneLabelCell"

    override func viewDidLoad() {
        super.viewDidLoad()

        theTableView.dataSource = self
        theTableView.delegate = self

        theTableView.register(MyOneLabelCell.self, forCellReuseIdentifier: reuseID)

        // add the views
        view.addSubview(theScrollView)
        theScrollView.addSubview(theContainerView)
        theContainerView.addSubview(theTableView)
        theContainerView.addSubview(theFooterView)

        // this will allow the container height to be at least the height of the scroll view
        // when enough content is added to the container, it will grow
        let containerHeightConstraint = theContainerView.heightAnchor.constraint(equalTo: theScrollView.heightAnchor, multiplier: 1.0)
        containerHeightConstraint.priority = .defaultLow

        NSLayoutConstraint.activate([

            // constrain scrollView to all 4 sides (safe-area)
            theScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            theScrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            theScrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            theScrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),

            // constrain containerView to all 4 sides of scrollView
            theContainerView.topAnchor.constraint(equalTo: theScrollView.topAnchor),
            theContainerView.bottomAnchor.constraint(equalTo: theScrollView.bottomAnchor),
            theContainerView.leadingAnchor.constraint(equalTo: theScrollView.leadingAnchor),
            theContainerView.trailingAnchor.constraint(equalTo: theScrollView.trailingAnchor),

            theContainerView.widthAnchor.constraint(equalTo: theScrollView.widthAnchor),

            // constrain tableView to top/leading/trailing of constainerView
            theTableView.topAnchor.constraint(equalTo: theContainerView.topAnchor),
            theTableView.leadingAnchor.constraint(equalTo: theContainerView.leadingAnchor),
            theTableView.trailingAnchor.constraint(equalTo: theContainerView.trailingAnchor),

            // constrain footerView >= 20 from bottom of tableView
            theFooterView.topAnchor.constraint(greaterThanOrEqualTo: theTableView.bottomAnchor, constant: 20.0),

            theFooterView.leadingAnchor.constraint(equalTo: theContainerView.leadingAnchor, constant: 0.0),
            theFooterView.trailingAnchor.constraint(equalTo: theContainerView.trailingAnchor, constant: 0.0),
            theFooterView.bottomAnchor.constraint(equalTo: theContainerView.bottomAnchor, constant: 0.0),

            theFooterView.heightAnchor.constraint(equalToConstant: 150.0),

            containerHeightConstraint,

            ])

    }


    // MARK: - Table view data source

    func numberOfSections(in tableView: UITableView) -> Int {
        return numSections
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return section < 2 ? 1 : 2
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: reuseID, for: indexPath) as! MyOneLabelCell

        switch indexPath.section {
        case 0:
            cell.theLabel.text = "Add a section"
        case 1:
            cell.theLabel.text = "Delete a section"
        default:
            cell.theLabel.text = "\(indexPath)"
        }

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        switch indexPath.section {
        case 0:
            numSections += 1
            tableView.reloadData()
        case 1:
            if numSections > 2 {
                numSections -= 1
                tableView.reloadData()
            }
        default:
            print("\(indexPath) was selected")
        }

    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Section \(section) Header"
    }

    func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
        return "Section \(section) Footer"
    }

}

这篇关于如何将tableFooterView始终保持在UITableView的底部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆