为 UITableView Footer Swft 设置约束
Setting constraints to UITableView Footer Swft
我试图用 UIButton 放置页脚,现在我只用 frame 放置它,当我在 apple 的 iphones 上尝试它时,按钮没有放置好,因为我没有设置任何自动布局,问题是我试图将自动布局设置为页脚,但它一直失败,我也不确定是否可以这样做,希望得到方便的帮助,甚至是提示:)。
这是我的 tableView 代码:
import UIKit
import PanModal
protocol FilterTableViewDelegate {
func didUpdateSelectedDate(_ date: Date)
}
class FilterTableViewController: UITableViewController, PanModalPresentable {
var panScrollable: UIScrollView? {
return tableView
}
var albumsPickerIndexPath: IndexPath? // indexPath of the currently shown albums picker in tableview.
var delegate: FilterTableViewDelegate?
var datesCell = DatesCell()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
// registerTableViewCells()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// tableView.frame = view.bounds
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - View Configurations
func setupTableView() {
tableView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
tableView.separatorStyle = .singleLine
tableView.isScrollEnabled = false
tableView.allowsSelection = true
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 600
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
}
func indexPathToInsertDatePicker(indexPath: IndexPath) -> IndexPath {
if let albumsPickerIndexPath = albumsPickerIndexPath, albumsPickerIndexPath.row < indexPath.row {
return indexPath
} else {
return IndexPath(row: indexPath.row + 1, section: indexPath.section)
}
}
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// If datePicker is already present, we add one extra cell for that
if albumsPickerIndexPath != nil {
return 5 + 1
} else {
return 5
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let byActivityCell = UINib(nibName: "byActivityCell",bundle: nil)
self.tableView.register(byActivityCell,forCellReuseIdentifier: "byActivityCell")
let activityCell = tableView.dequeueReusableCell(withIdentifier: "byActivityCell", for: indexPath) as! byActivityCell
activityCell.selectionStyle = .none
return activityCell
case 1:
let byTypeCell = UINib(nibName: "ByType",bundle: nil)
self.tableView.register(byTypeCell,forCellReuseIdentifier: "byTypeCell")
let typeCell = tableView.dequeueReusableCell(withIdentifier: "byTypeCell", for: indexPath) as! ByType
typeCell.selectionStyle = .none
return typeCell
case 2:
let byHashtagsCell = UINib(nibName: "ByHashtags",bundle: nil)
self.tableView.register(byHashtagsCell,forCellReuseIdentifier: "byHashtagsCell")
let hashtagsCell = tableView.dequeueReusableCell(withIdentifier: "byHashtagsCell", for: indexPath) as! ByHashtags
hashtagsCell.selectionStyle = .none
return hashtagsCell
case 3:
let byDatesCell = UINib(nibName: "DatesCell",bundle: nil)
self.tableView.register(byDatesCell,forCellReuseIdentifier: "byDatesCell")
let datesCell = tableView.dequeueReusableCell(withIdentifier: "byDatesCell", for: indexPath) as! DatesCell
datesCell.selectionStyle = .none
datesCell.datesTableViewCellDelegate = self
return datesCell
case 4:
let byAlbumCell = UINib(nibName: "AlbumCell",bundle: nil)
self.tableView.register(byAlbumCell,forCellReuseIdentifier: "byAlbumCell")
let albumCell = tableView.dequeueReusableCell(withIdentifier: "byAlbumCell", for: indexPath) as! AlbumCell
albumCell.configureCell(choosenAlbum: "Any")
albumCell.selectionStyle = .none
return albumCell
case 5:
let albumPickerCell = UINib(nibName: "AlbumsPickerTableViewCell", bundle: nil)
self.tableView.register(albumPickerCell, forCellReuseIdentifier: "albumPickerCell")
let albumsPicker = tableView.dequeueReusableCell(withIdentifier: "albumPickerCell", for: indexPath) as! AlbumsPickerTableViewCell
return albumsPicker
default:
return UITableViewCell()
}
}
// MARK: - footer Methods:
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return getfooterView()
}
func getfooterView() -> UIView
{
let footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 400))
let applyFiltersBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 380, height: 40))
applyFiltersBtn.center = footerView.center
applyFiltersBtn.layer.cornerRadius = 12
applyFiltersBtn.layer.masksToBounds = true
applyFiltersBtn.setTitle("Apply Filters", for: .normal)
applyFiltersBtn.backgroundColor = #colorLiteral(red: 0.1957295239, green: 0.6059523225, blue: 0.960457623, alpha: 1)
// doneButton.addTarget(self, action: #selector(hello(sender:)), for: .touchUpInside)
footerView.addSubview(applyFiltersBtn)
return footerView
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10
}
// MARK: TableViewDelegate Methods:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
tableView.beginUpdates()
// 1 - We Delete the UIPicker when the user "Deselect" thr row.
if let datePickerIndexPath = albumsPickerIndexPath, datePickerIndexPath.row - 1 == indexPath.row {
tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
self.albumsPickerIndexPath = nil
} else {
// 2
// if let datePickerIndexPath = albumsPickerIndexPath {
// tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
// }
albumsPickerIndexPath = indexPathToInsertDatePicker(indexPath: indexPath)
tableView.insertRows(at: [albumsPickerIndexPath!], with: .fade)
tableView.deselectRow(at: indexPath, animated: true)
}
tableView.endUpdates()
}
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if indexPath.row == 4 {
return indexPath
} else {
return nil
}
}
}
extension FilterTableViewController: DatesTableViewCellDelegate {
func didButtonFromPressed() {
print("Button From is Pressed")
let pickerController = CalendarPickerViewController(
baseDate: Date(),
selectedDateChanged: { [weak self] date in
guard let self = self else { return }
// self.item.date = date
//TODO: Pass the date to the DatesCell to update the UIButtons.
self.delegate?.didUpdateSelectedDate(date)
self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
})
present(pickerController, animated: true, completion: nil)
}
func didButtonToPressed() {
//TODO: Present our custom calendar
let calendarController = CalendarPickerViewController(
baseDate: Date(),
selectedDateChanged: { [weak self] date in
guard let self = self else { return }
self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
})
self.present(calendarController, animated: true, completion: nil)
}
}
首先,一个提示(基于这个问题和您 post 编辑的其他问题):简化您正在做的事情。当您 post 引用 5 个不同单元格的代码 classes 以及用于处理单元格操作委托、插入和删除等的代码时... but你的问题是关于Section Footer布局的,很难提供帮助。
因此,这是一个简单的 table 视图控制器示例,使用默认单元格 class 和自定义 UITableViewHeaderFooterView
部分页脚:
class MySectionFooterView: UITableViewHeaderFooterView {
let applyFiltersBtn = UIButton()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
configureContents()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configureContents()
}
func configureContents() {
applyFiltersBtn.translatesAutoresizingMaskIntoConstraints = false
applyFiltersBtn.layer.cornerRadius = 12
applyFiltersBtn.layer.masksToBounds = true
applyFiltersBtn.setTitle("Apply Filters", for: .normal)
applyFiltersBtn.backgroundColor = #colorLiteral(red: 0.1957295239, green: 0.6059523225, blue: 0.960457623, alpha: 1)
contentView.addSubview(applyFiltersBtn)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// use layoutMarginsGuide for top and bottom
applyFiltersBtn.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
applyFiltersBtn.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
// inset button 20-pts from layoutMarginsGuide on each side
applyFiltersBtn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
applyFiltersBtn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// make button height 40-pts
applyFiltersBtn.heightAnchor.constraint(equalToConstant: 40.0),
])
}
}
class SectionFooterViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// register cell class for reuse
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
// register Section Footer class for reuse
tableView.register(MySectionFooterView.self, forHeaderFooterViewReuseIdentifier: "mySectionFooter")
tableView.sectionFooterHeight = UITableView.automaticDimension
tableView.estimatedSectionFooterHeight = 50
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "\(indexPath)"
return cell
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = tableView.dequeueReusableHeaderFooterView(withIdentifier:"mySectionFooter") as! MySectionFooterView
return view
}
}
要处理点击部分页脚中的按钮,您可以在 viewForFooterInSection
中为它分配一个动作,或者将该动作构建到 MySectionFooterView
class 中并使用闭包或协议/委托模式。
我试图用 UIButton 放置页脚,现在我只用 frame 放置它,当我在 apple 的 iphones 上尝试它时,按钮没有放置好,因为我没有设置任何自动布局,问题是我试图将自动布局设置为页脚,但它一直失败,我也不确定是否可以这样做,希望得到方便的帮助,甚至是提示:)。 这是我的 tableView 代码:
import UIKit
import PanModal
protocol FilterTableViewDelegate {
func didUpdateSelectedDate(_ date: Date)
}
class FilterTableViewController: UITableViewController, PanModalPresentable {
var panScrollable: UIScrollView? {
return tableView
}
var albumsPickerIndexPath: IndexPath? // indexPath of the currently shown albums picker in tableview.
var delegate: FilterTableViewDelegate?
var datesCell = DatesCell()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
// registerTableViewCells()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// tableView.frame = view.bounds
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - View Configurations
func setupTableView() {
tableView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
tableView.separatorStyle = .singleLine
tableView.isScrollEnabled = false
tableView.allowsSelection = true
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 600
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
}
func indexPathToInsertDatePicker(indexPath: IndexPath) -> IndexPath {
if let albumsPickerIndexPath = albumsPickerIndexPath, albumsPickerIndexPath.row < indexPath.row {
return indexPath
} else {
return IndexPath(row: indexPath.row + 1, section: indexPath.section)
}
}
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// If datePicker is already present, we add one extra cell for that
if albumsPickerIndexPath != nil {
return 5 + 1
} else {
return 5
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let byActivityCell = UINib(nibName: "byActivityCell",bundle: nil)
self.tableView.register(byActivityCell,forCellReuseIdentifier: "byActivityCell")
let activityCell = tableView.dequeueReusableCell(withIdentifier: "byActivityCell", for: indexPath) as! byActivityCell
activityCell.selectionStyle = .none
return activityCell
case 1:
let byTypeCell = UINib(nibName: "ByType",bundle: nil)
self.tableView.register(byTypeCell,forCellReuseIdentifier: "byTypeCell")
let typeCell = tableView.dequeueReusableCell(withIdentifier: "byTypeCell", for: indexPath) as! ByType
typeCell.selectionStyle = .none
return typeCell
case 2:
let byHashtagsCell = UINib(nibName: "ByHashtags",bundle: nil)
self.tableView.register(byHashtagsCell,forCellReuseIdentifier: "byHashtagsCell")
let hashtagsCell = tableView.dequeueReusableCell(withIdentifier: "byHashtagsCell", for: indexPath) as! ByHashtags
hashtagsCell.selectionStyle = .none
return hashtagsCell
case 3:
let byDatesCell = UINib(nibName: "DatesCell",bundle: nil)
self.tableView.register(byDatesCell,forCellReuseIdentifier: "byDatesCell")
let datesCell = tableView.dequeueReusableCell(withIdentifier: "byDatesCell", for: indexPath) as! DatesCell
datesCell.selectionStyle = .none
datesCell.datesTableViewCellDelegate = self
return datesCell
case 4:
let byAlbumCell = UINib(nibName: "AlbumCell",bundle: nil)
self.tableView.register(byAlbumCell,forCellReuseIdentifier: "byAlbumCell")
let albumCell = tableView.dequeueReusableCell(withIdentifier: "byAlbumCell", for: indexPath) as! AlbumCell
albumCell.configureCell(choosenAlbum: "Any")
albumCell.selectionStyle = .none
return albumCell
case 5:
let albumPickerCell = UINib(nibName: "AlbumsPickerTableViewCell", bundle: nil)
self.tableView.register(albumPickerCell, forCellReuseIdentifier: "albumPickerCell")
let albumsPicker = tableView.dequeueReusableCell(withIdentifier: "albumPickerCell", for: indexPath) as! AlbumsPickerTableViewCell
return albumsPicker
default:
return UITableViewCell()
}
}
// MARK: - footer Methods:
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return getfooterView()
}
func getfooterView() -> UIView
{
let footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 400))
let applyFiltersBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 380, height: 40))
applyFiltersBtn.center = footerView.center
applyFiltersBtn.layer.cornerRadius = 12
applyFiltersBtn.layer.masksToBounds = true
applyFiltersBtn.setTitle("Apply Filters", for: .normal)
applyFiltersBtn.backgroundColor = #colorLiteral(red: 0.1957295239, green: 0.6059523225, blue: 0.960457623, alpha: 1)
// doneButton.addTarget(self, action: #selector(hello(sender:)), for: .touchUpInside)
footerView.addSubview(applyFiltersBtn)
return footerView
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10
}
// MARK: TableViewDelegate Methods:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
tableView.beginUpdates()
// 1 - We Delete the UIPicker when the user "Deselect" thr row.
if let datePickerIndexPath = albumsPickerIndexPath, datePickerIndexPath.row - 1 == indexPath.row {
tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
self.albumsPickerIndexPath = nil
} else {
// 2
// if let datePickerIndexPath = albumsPickerIndexPath {
// tableView.deleteRows(at: [datePickerIndexPath], with: .fade)
// }
albumsPickerIndexPath = indexPathToInsertDatePicker(indexPath: indexPath)
tableView.insertRows(at: [albumsPickerIndexPath!], with: .fade)
tableView.deselectRow(at: indexPath, animated: true)
}
tableView.endUpdates()
}
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if indexPath.row == 4 {
return indexPath
} else {
return nil
}
}
}
extension FilterTableViewController: DatesTableViewCellDelegate {
func didButtonFromPressed() {
print("Button From is Pressed")
let pickerController = CalendarPickerViewController(
baseDate: Date(),
selectedDateChanged: { [weak self] date in
guard let self = self else { return }
// self.item.date = date
//TODO: Pass the date to the DatesCell to update the UIButtons.
self.delegate?.didUpdateSelectedDate(date)
self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
})
present(pickerController, animated: true, completion: nil)
}
func didButtonToPressed() {
//TODO: Present our custom calendar
let calendarController = CalendarPickerViewController(
baseDate: Date(),
selectedDateChanged: { [weak self] date in
guard let self = self else { return }
self.tableView.reloadRows(at: [IndexPath(row: 3, section: 0)], with: .fade)
})
self.present(calendarController, animated: true, completion: nil)
}
}
首先,一个提示(基于这个问题和您 post 编辑的其他问题):简化您正在做的事情。当您 post 引用 5 个不同单元格的代码 classes 以及用于处理单元格操作委托、插入和删除等的代码时... but你的问题是关于Section Footer布局的,很难提供帮助。
因此,这是一个简单的 table 视图控制器示例,使用默认单元格 class 和自定义 UITableViewHeaderFooterView
部分页脚:
class MySectionFooterView: UITableViewHeaderFooterView {
let applyFiltersBtn = UIButton()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
configureContents()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configureContents()
}
func configureContents() {
applyFiltersBtn.translatesAutoresizingMaskIntoConstraints = false
applyFiltersBtn.layer.cornerRadius = 12
applyFiltersBtn.layer.masksToBounds = true
applyFiltersBtn.setTitle("Apply Filters", for: .normal)
applyFiltersBtn.backgroundColor = #colorLiteral(red: 0.1957295239, green: 0.6059523225, blue: 0.960457623, alpha: 1)
contentView.addSubview(applyFiltersBtn)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// use layoutMarginsGuide for top and bottom
applyFiltersBtn.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
applyFiltersBtn.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
// inset button 20-pts from layoutMarginsGuide on each side
applyFiltersBtn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
applyFiltersBtn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// make button height 40-pts
applyFiltersBtn.heightAnchor.constraint(equalToConstant: 40.0),
])
}
}
class SectionFooterViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// register cell class for reuse
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
// register Section Footer class for reuse
tableView.register(MySectionFooterView.self, forHeaderFooterViewReuseIdentifier: "mySectionFooter")
tableView.sectionFooterHeight = UITableView.automaticDimension
tableView.estimatedSectionFooterHeight = 50
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "\(indexPath)"
return cell
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = tableView.dequeueReusableHeaderFooterView(withIdentifier:"mySectionFooter") as! MySectionFooterView
return view
}
}
要处理点击部分页脚中的按钮,您可以在 viewForFooterInSection
中为它分配一个动作,或者将该动作构建到 MySectionFooterView
class 中并使用闭包或协议/委托模式。