如何将tableFooterView始终保持在UITableView的底部 [英] How to keep tableFooterView always on bottom of 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:
但是,当前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屋!