[英]How to keep tableFooterView always on bottom of UITableView
I have a UITableView
with a variable amount of sections.我有一个
UITableView
具有可变数量的部分。 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.我的
UITableView
也有一个tableFooterView
,我想一直保留在屏幕底部,除非表格太大而无法显示在屏幕上,那么tableFooterView
应该显示在最后一部分的下方。 What I want to accomplish is illustrated here:我想要完成的事情如下所示:
Example of what I want, scenario 1我想要的例子,场景 1
Example of what I want, scenario 2我想要的例子,场景 2
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:然而,目前
tableFooterView
总是位于最后一部分的正下方,所以当只有两个部分时,它看起来像这样:
Example of what I currently have我目前拥有的示例
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.我一直在环顾四周,因为 Apple 不支持 tableFooterView 的
tableFooterView
,我还没有找到解决方案。 Similar cases replace the tableFooterView
with a sectionFooter
on the last section, but I can't do that as I already have sectionFooters
.类似的案例更换
tableFooterView
用sectionFooter
在最后一节,但我不能这样做,因为我已经有sectionFooters
。
Is there anybody who can help me out or point me towards the right direction?有没有人可以帮助我或为我指明正确的方向? A couple of things to consider:
需要考虑以下几点:
tableFooterView
;tableFooterView
;UITableView
and rows to the sections, so the tableFooterView should then update its locationUITableView
添加部分并向这些部分添加行,因此 tableFooterView 应该更新其位置How I set up the tableFooterView
at the moment:我目前如何设置
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
}
}
EDIT: Tried the scrollViewDidScroll
method as suggested, but didn't work:编辑:按照建议尝试了
scrollViewDidScroll
方法,但没有奏效:
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)
}
}
}
One approach would be:一种方法是:
UIView
for the "footer" view in a "container" viewUIView
>=
to the bottom of the table view>=
约束到表视图的底部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.所以,tableView的“auto-height”+footer view的高度决定了container view的高度,它决定了scroll view的
.contentSize
。 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.
当滚动视图有足够的内容时,它会“下推”页脚视图。
Example:例子:
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
:一切都是通过代码完成的……不需要 IBOutlets,所以只需创建一个新的视图控制器并将其类分配给
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"
}
}
You might be able to do this by manually translating the frame of the footer view when you scroll the table.您可以通过在滚动表格时手动平移页脚视图的框架来完成此操作。 You will need to do the following:
您需要执行以下操作:
tableFooterView
.tableFooterView
。scrollViewDidScroll
method of UIScrollViewDelegate.scrollViewDidScroll
方法。tableView.tableFooterView?.transform = CGAffineTransform(translationX: 0, y: <some value>)
tableView.tableFooterView?.transform = CGAffineTransform(translationX: 0, y: <some value>)
I made a demo in Github: StickTableFooterView我在 Github 做了一个演示: StickTableFooterView
-----Updated------ - - -更新 - - -
Setting proper layout constraints:设置适当的布局约束:
innerView.translatesAutoresizingMaskIntoConstraints = false;
[NSLayoutConstraint constraintWithItem:innerView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:innerView.superview attribute:NSLayoutAttributeRight multiplier:1 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:innerView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:innerView.superview attribute:NSLayoutAttributeLeft multiplier:1 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:innerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:innerView.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:innerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.tableView.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:innerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:innerViewHeight].active = YES;
The key point is set layout constraint with tableView.superview
:关键点是使用
tableView.superview
设置布局约束:
[NSLayoutConstraint constraintWithItem:innerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.tableView.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:0].active = YES;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.