Когда я должен пересчитать размер нижнего колонтитула UITableView? - PullRequest
0 голосов
/ 04 октября 2018

Я хотел бы пересчитать высоту нижнего колонтитула табличного представления на основе изменяющегося размера содержимого табличного представления.Когда таблица имеет нулевые строки, высота нижнего колонтитула будет максимальной.По мере добавления строк в таблицу высота нижнего колонтитула будет уменьшаться до минимума.Я использую нижний колонтитул, чтобы заполнить пустое пространство, которое появляется в нижней части таблицы, когда есть ноль или несколько строк.В дополнение к добавляемым строкам размер содержимого может изменяться, поскольку высота (содержимое) существующей строки была изменена.

Предположим, у меня есть контроллер представления, основной вид которого содержит два подпредставления: aкнопка и вид таблицы.Нажатие на кнопку приводит к изменению хранилища данных и вызову метода reloadData таблицы.Когда / Где я должен назначить новое значение для tableFooterView.bounds.size.height таблицы?

Я также должен указать, что я использую UITableViewAutomaticDimension.Если в методе делегата источника данных таблицы cellForRowAt я печатаю высоту ячейки, которую я получаю:

Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 44.0

Все 21, кроме последнего, нового.Это должно произойти из-за того, что автоматическое определение размеров еще не применено.

Обновление:

Я предварительно пришел к следующему решению (большое спасибо всем людям на this нить для большей части решения).Я пробный, потому что решение включает в себя вызов reloadData дважды, чтобы решить проблему с contentSize.См. этот проект GitHub для демонстрации проблемы contentSize.

class TableView: UITableView {

    override func reloadData() {
        execute() { super.reloadData() }
    }

    override func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) {
        execute() { super.reloadRows(at: indexPaths, with: animation) }
    }

    private func execute(reload: @escaping () -> Void) {
        CATransaction.begin()
        CATransaction.setCompletionBlock() {
            if self.adjustFooter() {
                reload() // Cause the contentSize to update (see GitHub project)
                self.layoutIfNeeded()
            }
        }
        reload()
        CATransaction.commit()
    }

    // Return true(false) if the footer was(was not) adjusted
    func adjustFooter() -> Bool {
        guard let currentFrame = tableFooterView?.frame else { return false }

        let newHeight = calcFooterHeight()
        let adjustmentNeeded = newHeight != currentFrame.height

        if adjustmentNeeded {
            tableFooterView?.frame = CGRect(x: currentFrame.minX, y: currentFrame.minY, width: currentFrame.width, height: newHeight)
        }

        return adjustmentNeeded
    }

    private let minFooterHeight: CGFloat = 44
    private func calcFooterHeight() -> CGFloat {
        guard let footerView = tableFooterView else { return 0 }

        let spaceTaken = contentSize.height - footerView.bounds.height
        let spaceAvailable = bounds.height - spaceTaken
        return spaceAvailable > minFooterHeight ? spaceAvailable : minFooterHeight
    }
}

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Я пришел к следующему решению.Огромное спасибо всем участникам этой ветки за большую часть решения.Класс TableViewController.TableView обеспечивает желаемую функциональность.Оставшаяся часть кода завершает полный пример.

//
//  TableViewController.swift
//  Tables
//
//  Created by Robert Vaessen on 11/6/18.
//  Copyright © 2018 Robert Vaessen. All rights reserved.
//
//  Note: Add the following to AppDelegate:
//
//    func application(_ application: UIApplication,
//                    didFinishLaunchingWithOptions launchOptions: 
//                    [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//        window = UIWindow(frame: UIScreen.main.bounds)
//        window?.makeKeyAndVisible()
//        window?.rootViewController = TableViewController()
//        return true
//    }


import UIKit

class TableViewController: UIViewController {

    class TableView : UITableView {

        override func reloadData() {
            execute() { super.reloadData() }
        }

        override func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) {
            execute() { super.reloadRows(at: indexPaths, with: animation) }
        }

        private func execute(reload: @escaping () -> Void) {
            CATransaction.begin()
            CATransaction.setCompletionBlock() {
                print("Reload completed")
                _ = self.adjustFooter()
            }
            print("\nReload begun")
            reload()
            CATransaction.commit()
        }

        private func adjustFooter() -> Bool {
            guard let footerView = tableFooterView else { return false }

            func calcFooterHeight() -> CGFloat {
                var heightUsed = tableHeaderView?.bounds.height ?? 0
                for cell in visibleCells { heightUsed += cell.bounds.height }
                let heightRemaining = bounds.height - heightUsed

                let minHeight: CGFloat = 44
                return heightRemaining > minHeight ? heightRemaining : minHeight
            }

            let newHeight = calcFooterHeight()
            guard newHeight != footerView.bounds.height else { return false }

            // Keep the origin where it is, i.e. tweaking just the height expands the frame about its center
            let currentFrame = footerView.frame
            footerView.frame = CGRect(x: currentFrame.origin.x, y: currentFrame.origin.y, width: currentFrame.width, height: newHeight)

            return true
        }
    }

    class FooterView : UIView {
        override func draw(_ rect: CGRect) {
            print("Drawing footer")
            super.draw(rect)
        }
    }

    private var tableView: TableView!

    private let cellReuseId = "TableCell"
    private let data: [UIColor] = [UIColor(white: 0.4, alpha: 1), UIColor(white: 0.5, alpha: 1), UIColor(white: 0.6, alpha: 1), UIColor(white: 0.7, alpha: 1)]
    private var dataRepeatCount = 1

    override func viewDidLoad() {
        super.viewDidLoad()

        func createTable(in: UIView) -> TableView {

            let tableView = TableView(frame: CGRect.zero)

            tableView.separatorStyle = .none
            tableView.translatesAutoresizingMaskIntoConstraints = false

            `in`.addSubview(tableView)

            tableView.centerXAnchor.constraint(equalTo: `in`.centerXAnchor).isActive = true
            tableView.centerYAnchor.constraint(equalTo: `in`.centerYAnchor).isActive = true
            tableView.widthAnchor.constraint(equalTo: `in`.widthAnchor, multiplier: 1).isActive = true
            tableView.heightAnchor.constraint(equalTo: `in`.heightAnchor, multiplier: 0.8).isActive = true

            tableView.dataSource = self
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseId)

            return tableView
        }

        func addHeader(to: UITableView) {
            let header = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 50))
            to.tableHeaderView = header

            let color = UIColor.black
            let offset: CGFloat = 64

            let add = UIButton(type: .system)
            add.setTitle("Add", for: .normal)
            add.layer.borderColor = color.cgColor
            add.layer.borderWidth = 1
            add.layer.cornerRadius = 5
            add.tintColor = color
            add.contentEdgeInsets = UIEdgeInsets.init(top: 8, left: 8, bottom: 8, right: 8)
            add.addTarget(self, action: #selector(addRows), for: .touchUpInside)
            add.translatesAutoresizingMaskIntoConstraints = false
            header.addSubview(add)
            add.centerXAnchor.constraint(equalTo: to.centerXAnchor, constant: -offset).isActive = true
            add.centerYAnchor.constraint(equalTo: header.centerYAnchor).isActive = true

            let remove = UIButton(type: .system)
            remove.setTitle("Remove", for: .normal)
            remove.layer.borderColor = color.cgColor
            remove.layer.borderWidth = 1
            remove.layer.cornerRadius = 5
            remove.tintColor = color
            remove.contentEdgeInsets = UIEdgeInsets.init(top: 8, left: 8, bottom: 8, right: 8)
            remove.addTarget(self, action: #selector(removeRows), for: .touchUpInside)
            remove.translatesAutoresizingMaskIntoConstraints = false
            header.addSubview(remove)
            remove.centerXAnchor.constraint(equalTo: header.centerXAnchor, constant: offset).isActive = true
            remove.centerYAnchor.constraint(equalTo: header.centerYAnchor).isActive = true
        }

        func addFooter(to: UITableView) {
            let footer = FooterView(frame: CGRect(x: 0, y: 0, width: 0, height: 50))
            footer.layer.borderWidth = 3
            footer.layer.borderColor = UIColor.red.cgColor
            //footer.contentMode = .redraw
            to.tableFooterView = footer
        }

        tableView = createTable(in: view)
        addHeader(to: tableView)
        addFooter(to: tableView)

        view.backgroundColor = .white
        tableView.backgroundColor = .black // UIColor(white: 0.2, alpha: 1)
        tableView.tableHeaderView!.backgroundColor = .cyan // UIColor(white: 0, alpha: 1)
        tableView.tableFooterView!.backgroundColor = .white // UIColor(white: 0, alpha: 1)
    }

    @objc private func addRows() {
        dataRepeatCount += 1
        tableView.reloadData()
    }

    @objc private func removeRows() {
        dataRepeatCount -= dataRepeatCount > 0 ? 1 : 0
        tableView.reloadData()
    }
}

extension TableViewController : UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard section == 0 else { fatalError("Unexpected section: \(section)") }
        return dataRepeatCount * data.count
    }

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

        cell.textLabel?.textAlignment = .center
        cell.backgroundColor = data[indexPath.row % data.count]
        cell.textLabel?.textColor = .white
        cell.textLabel?.text = "\(indexPath.row)"

        return cell
    }
}
0 голосов
/ 05 октября 2018

UITableViewDelegate имеет метод tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat, который мы можем использовать для указания высоты нижних колонтитулов.Этот метод срабатывает, когда мы вызываем reloadData() для представления таблицы или когда ориентация экрана была изменена и т. Д.

Таким образом, вы можете реализовать этот метод для вычисления новой высоты нижнего колонтитула:

    override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {

    guard section == 0 else { return 0.0 } // assume there is only one section in the table

    var cellsHeight: CGFloat = 0.0

    let rows = self.tableView(tableView, numberOfRowsInSection: section)

    for row in 0..<rows
    {
        let indexPath = IndexPath(item: row, section: section)
        cellsHeight += self.tableView(tableView, heightForRowAt: indexPath)
    }

    let headerHeight: CGFloat = tableView.tableHeaderView?.frame.height ?? 0.0
    let footerHeight = view.frame.height - headerHeight - cellsHeight

    return footerHeight
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...