4

我有UITableView一个可变数量的部分。每个部分都有可变数量的单元格,每个部分都有页眉和页脚。我UITableView也有一个tableFooterView我想一直放在屏幕底部的,除非表格太大而不能放在屏幕上,然后tableFooterView应该显示在最后一部分的下方。我想要完成的说明如下:

我想要的示例,场景 1

我想要的示例,场景 2

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

我目前拥有的示例

我正在寻找一种方法,使其在所有可能的情况下始终处于底部。我一直在环顾四周,因为 Apple 不支持 AutoLayout,所以tableFooterView我还没有找到解决方案。类似的情况在最后一部分tableFooterView用 a替换sectionFooter,但我不能这样做,因为我已经有了sectionFooters.

有没有人可以帮助我或指出我正确的方向?有几点需要考虑:

  • 它必须是一个tableFooterView
  • 用户可以将部分添加到部分UITableView并将行添加到部分,因此 tableFooterView 应该随后更新其位置

我现在是如何设置的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建议的方法,但没有奏效:

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)

        }

    }

}
4

3 回答 3

2

一种方法是:

  • 使用扩展来定义“自定大小非滚动”表视图
  • 在“容器”视图中嵌入表格视图和UIView“页脚”视图的法线
  • 将容器视图嵌入到滚动视图中,高度等于滚动视图但优先级较低
  • 将页脚视图约束到容器视图>=的底部,以及表格视图的底部

因此,tableView 的“自动高度”+ 页脚视图的高度决定了容器视图的高度,这决定.contentSize了滚动视图的高度。页脚视图将“粘”到容器视图的底部。当滚动视图有足够的内容时,它会“下推”页脚视图。

例子:

在此处输入图像描述

在此处输入图像描述

这是创建它的代码。一切都是通过代码完成的......不需要 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"
    }

}
于 2019-05-14T15:34:56.503 回答
0

我在 Github 做了一个演示:StickTableFooterView

截屏

如何让 tableFooterView 始终位于 UITableView 的底部?

  1. 将视图创建为 tableFooterView。
  2. 创建一个视图并将其作为子视图(Pretended tableFooterView)放入 tableFooterView。
  3. 设置适当的布局约束。
  4. 享受你的 tableFooterView 在 UITableView 底部的滴答声。
  • 因为伪装的 tableFooterView 超出了它的 superview 的范围,需要处理 touch 事件。请参阅 ViewWithOutb​​oundsButtons.m。

关于演示

  • 红色区域指的是 tableFooterView。
  • 黄色区域指的是伪装的 tableFooterView。

- - -更新 - - -

设置适当的布局约束:

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;

关键点是设置布局约束tableView.superview

[NSLayoutConstraint constraintWithItem:innerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.tableView.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:0].active = YES;
于 2021-05-12T06:08:46.973 回答
0

您可以通过在滚动表格时手动翻译页脚视图的框架来做到这一点。您将需要执行以下操作:

  1. 将视图设置为tableFooterView.
  2. 响应scrollViewDidScrollUIScrollViewDelegate 的方法。
  3. 计算偏移页脚视图的量并将其设置为变换:tableView.tableFooterView?.transform = CGAffineTransform(translationX: 0, y: <some value>)
于 2019-05-14T15:11:58.817 回答