我正在尝试实现当表视图中没有项目时 uikit 内容不可用。配置看起来很简单,但我很困惑将其放置在哪里,因此它会显示表何时没有添加对象。
我尝试将配置放在有条件和无条件的 viewWillAppear 中。当放置在条件中时,添加时列表中不会出现任何对象。如果我删除条件,则会添加对象,但内容不可用信息仍然存在。
我也尝试在表视图中执行相同的操作,但得到了相同的结果。
这个应用程序非常简单,主要用于练习。我有两个视图控制器,第一个视图控制器应该有内容不可用。第二个视图控制器有一个表格视图中呈现的对象数组。如果用户增加步进器,这些对象将被传递到初始视图控制器,并且不可用的内容应该消失。对此的任何帮助表示赞赏。
第一个视图控制器
class KitViewController: UIViewController {
let user = User()
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
initializeTable()
title = "Gear"
}
// initialize a new empty array of essential objects assigned to tableItems
var tableItems = [Essential]()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if tableItems.count == 0 {
var config = UIContentUnavailableConfiguration.empty()
config.image = UIImage(systemName: "figure.hiking")
config.text = "You don't have any gear"
config.secondaryText = "Add items and get hiking"
self.contentUnavailableConfiguration = config
} else {
// assign the users table items to table items
tableItems = User.userTableItems(user: user)
tableView.reloadData()
}
}
@IBSegueAction func plusButtonTapped(_ coder: NSCoder) -> ListViewController? {
return ListViewController(coder: coder, user: user)
}
func initializeTable() {
tableView.delegate = self
tableView.dataSource = self
}
}
extension KitViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// if the no table items are in the essential array no table cells will appear
return tableItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.categoryReuseID, for: indexPath)
// setup the table cell
let kitItem = tableItems[indexPath.row]
var content = cell.defaultContentConfiguration()
content.text = "\(kitItem.count) \(kitItem.itemName)"
cell.contentConfiguration = content
return cell
}
}
第二个视图控制器
import UIKit
class ListViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var addCustomButton: UIBarButtonItem!
// pass the user to the list view
var user: User
init(coder: NSCoder, user: User) {
self.user = user
super.init(coder: coder)!
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: K.cellNibName, bundle: nil), forCellReuseIdentifier: K.categoryReuseID)
title = "Select Gear"
}
extension ListViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// returns the users datasource essential objects array
return user.datasource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.categoryReuseID, for: indexPath) as! MessageCell
let item = user.datasource[indexPath.row]
cell.item = item
cell.cellLabel.text = item.itemName
cell.cellStepper.value = Double(item.count)
cell.countLabel.text = String(item.count)
return cell
}
}
使用
UIContentUnavailableConfiguration
的做法是正确的,但关键是每当数据发生变化时更新配置并重新加载表视图。以下是修改代码的方法:
第一个视图控制器(KitViewController)
class KitViewController: UIViewController {
let user = User()
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
initializeTable()
title = "Gear"
}
var tableItems = [Essential]() {
didSet { // This will be called whenever tableItems is changed
updateContentUnavailableView()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Load data from User (assuming it's updated when you return from the second VC)
tableItems = User.userTableItems(user: user)
tableView.reloadData()
}
@IBSegueAction func plusButtonTapped(_ coder: NSCoder) -> ListViewController? {
return ListViewController(coder: coder, user: user)
}
func initializeTable() {
tableView.delegate = self
tableView.dataSource = self
}
// Function to update the content unavailable view
private func updateContentUnavailableView() {
if tableItems.isEmpty {
let config = UIContentUnavailableConfiguration.empty()
config.image = UIImage(systemName: "figure.hiking")
config.text = "You don't have any gear"
config.secondaryText = "Add items and get hiking"
tableView.backgroundView = nil // Remove any existing background view
tableView.separatorStyle = .none // Hide separators
tableView.backgroundView = UIView(frame: tableView.bounds) // Set up a background view
tableView.backgroundView?.backgroundColor = .systemBackground // Set background color
self.tableView.backgroundView?.addSubview(UILabel(text: "No Data", textColor: .label, font: .systemFont(ofSize: 20, weight: .bold), textAlignment: .center, numberOfLines: 0, adjustsFontSizeToFitWidth: true)) // Add a label
} else {
tableView.backgroundView = nil // Remove content unavailable view
tableView.separatorStyle = .singleLine // Restore separators
}
}
}
extension KitViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// if the no table items are in the essential array no table cells will appear
return tableItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: K.categoryReuseID, for: indexPath)
// setup the table cell
let kitItem = tableItems[indexPath.row]
var content = cell.defaultContentConfiguration()
content.text = "\(kitItem.count) \(kitItem.itemName)"
cell.contentConfiguration = content
return cell
}
}
说明:
tableItems
与 didSet
: 我们在 didSet
上使用属性观察器 (tableItems
),而不是单独的变量。每当 updateContentUnavailableView
的值发生变化时,该观察者都会自动调用 tableItems
。updateContentUnavailableView()
: 此函数检查 tableItems
是否为空。
contentUnavailableConfiguration
。contentUnavailableConfiguration
设置为 nil
以删除占位符视图。tableView.reloadData()
: 我们在tableView.reloadData()
中更新tableItems
后调用viewWillAppear
来刷新表格视图,并在需要时应用内容不可用配置。第二个视图控制器(ListViewController)
user
对象后,您需要通知第一个视图控制器更新其数据。 有几种常见的方法:
使用委托模式的示例:
// ListViewController.swift
protocol ListViewControllerDelegate: AnyObject {
func didUpdateItems()
}
class ListViewController: UIViewController {
// ... your other code ...
weak var delegate: ListViewControllerDelegate?
// Call this method when the user updates the items
func updateItems() {
// ... your logic for updating items ...
delegate?.didUpdateItems()
}
}
// KitViewController.swift
// ... (in your prepare(for segue:) method)
if let listVC = segue.destination as? ListViewController {
listVC.user = user
listVC.delegate = self
}
// ... (conform to ListViewControllerDelegate)
extension KitViewController: ListViewControllerDelegate {
func didUpdateItems() {
tableItems = User.userTableItems(user: user) // Update tableItems from the user object
tableView.reloadData()
}
}
通过实现其中一种通信模式,当数据更改时,第一个视图控制器将收到通知,从而允许其更新表视图并根据需要显示或隐藏内容不可用的视图。