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

Example of what I want, scenario 1

Example of what I want, scenario 2

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

Example of what I currently have

我正在寻找一种在任何可能的情况下始终将其保持在底部的方法。我一直在寻找,并且因为Apple不支持tableFooterView的AutoLayout,所以我还没有找到解决方案。在上一节中,类似的情况用tableFooterView替换了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)
    
            }
    
        }
    
    }
    

    最佳答案

    一种方法是:

  • 使用扩展定义“自动调整大小的非滚动”表视图
  • 将表视图和“页脚”视图的普通UIView嵌入“容器”视图
  • 将容器视图嵌入滚动视图中,高度与滚动视图相同,但优先级较低
  • 将页脚视图限制在容器视图的底部,并将>=限制在表视图的底部

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

    例:

    ios - 如何将tableFooterView始终保持在UITableView的底部-LMLPHP

    ios - 如何将tableFooterView始终保持在UITableView的底部-LMLPHP

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

    09-07 12:10