处理一个空的 UITableView。打印一个友好的消息

我有一个 UITableView,在某些情况下空是合法的 我更喜欢在屏幕上打印一个友好的信息,比如:

这个列表现在是空的

最简单的方法是什么?

93077 次浏览

一种方法是修改数据源,使其在行数为零时返回 1,并在 tableView:cellForRowAtIndexPath:方法中生成一个特殊用途的单元格(可能使用不同的单元格标识符)。

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger actualNumberOfRows = <calculate the actual number of rows>;
return (actualNumberOfRows  == 0) ? 1 : actualNumberOfRows;
}


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger actualNumberOfRows = <calculate the actual number of rows>;
if (actualNumberOfRows == 0) {
// Produce a special cell with the "list is now empty" message
}
// Produce the correct cell the usual way
...
}

如果您需要维护多个表视图控制器,这可能会有些复杂,因为有人最终会忘记插入一个零检查。更好的方法是创建一个单独的 UITableViewDataSource实现实现,该实现总是返回一个带有可配置消息的行(我们称之为 EmptyTableViewDataSource)。当表视图控制器管理的数据发生更改时,管理更改的代码将检查数据是否为空。如果它不是空的,则使用表视图控制器的常规数据源设置它; 否则,使用已配置了适当消息的 EmptyTableViewDataSource实例设置它。

UITableView 的 background View 属性是您的朋友。

viewDidLoadreloadData中的任何地方,您应该确定表是否为空,并使用包含 UILabel 的 UIView 更新 UITableView 的 backoundView 属性,或者将其设置为 nil。就是这样。

当然可以让 UITableView 的数据源执行双重任务,并返回一个特殊的“列表为空”单元格,这让我感觉像是一个组件。突然,numberOfRowsInSection:(NSInteger)section不得不计算其他部分的行数,以确保它们也是空的。您还需要创建一个包含空消息的特殊单元格。也不要忘记,您可能需要更改单元格的高度以容纳空消息。这一切都是可行的,但它似乎像创可贴上的创可贴。

首先,其他流行方法存在的问题。

背景视图

如果使用简单的将背景视图设置为 UILabel 的情况,则背景视图不能很好地居中。

显示邮件的单元格、页眉或页脚

这会干扰函数代码并引入奇怪的边缘情况。如果您希望完美地将消息集中在一起,那将增加另一个复杂性级别。

滚动您自己的表视图控制器

你失去了内置的功能,比如刷新控制,并且重新发明了轮子。

添加 UITableViewController 作为子视图控制器

我有一种感觉,你最终会在 iOS7 + 中遇到 contentInset 问题——为什么要把事情复杂化呢?

我的解决办法

我想到的最好的解决方案(当然,这并不理想)是创建一个特殊的视图,它可以位于滚动视图的顶部并相应地执行操作。这在 iOS7中显然会变得复杂,因为它充斥着疯狂的内容,但是这是可行的。

你必须小心的事情:

  • 在 reloadData 期间的某个时候,表分隔符会出现在前面——您需要防止这种情况发生
  • ContentInset/contentOffset-在您的特殊视图中观察这些键
  • 键盘-如果你不想让键盘挡道,那就是另一种计算方式了
  • 自动布局-你不能依靠帧的变化来定位你的视图

一旦您在一个 UIView 子类中解决了这个问题,您就可以将其用于所有事情——加载旋转器、禁用视图、显示错误消息等等。

为此,我一直在使用 title ForFooterInSection 消息。我不知道这是否是次优的,但它的工作。

-(NSString*)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section   {


NSString *message = @"";
NSInteger numberOfRowsInSection = [self tableView:self.tableView numberOfRowsInSection:section ];


if (numberOfRowsInSection == 0) {
message = @"This list is now empty";
}


return message;
}

我只能建议将 UITextView 拖放到 TableView 的单元格后面。建立一个到 ViewController 的连接,并在适当的时候隐藏/显示它(例如,只要表重新加载)。

enter image description here

基于这里的答案,这里是我做的一个快速类,你可以在你的 UITableViewController中使用。

import Foundation
import UIKit


class TableViewHelper {


class func EmptyMessage(message:String, viewController:UITableViewController) {
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height))
let messageLabel = UILabel(frame: rect)
messageLabel.text = message
messageLabel.textColor = UIColor.blackColor()
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .Center;
messageLabel.font = UIFont(name: "TrebuchetMS", size: 15)
messageLabel.sizeToFit()


viewController.tableView.backgroundView = messageLabel;
viewController.tableView.separatorStyle = .None;
}
}

在你的 UITableViewController中,你可以在 numberOfSectionsInTableView(tableView: UITableView) -> Int中调用这个

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if projects.count > 0 {
return 1
} else {
TableViewHelper.EmptyMessage("You don't have any projects yet.\nYou can create up to 10.", viewController: self)
return 0
}
}

enter image description here

http://www.appcoda.com/pull-to-refresh-uitableview-empty/的帮助下

我推荐以下库: DZNEmptyDataSet

最简单的方法添加到您的项目是使用它与可可豆喜欢这样: pod 'DZNEmptyDataSet'

在 TableViewController 中添加以下 import 语句(Swift) :

import DZNEmptyDataSet

然后确保你的课程符合 DNZEmptyDataSetSourceDZNEmptyDataSetDelegate的要求,如下所示:

class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate

viewDidLoad中添加以下代码行:

tableView.emptyDataSetSource = self
tableView.emptyDataSetDelegate = self
tableView.tableFooterView = UIView()

现在你要做的就是展示这种空状态:

//Add title for empty dataset
func titleForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let str = "Welcome"
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)]
return NSAttributedString(string: str, attributes: attrs)
}


//Add description/subtitle on empty dataset
func descriptionForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let str = "Tap the button below to add your first grokkleglob."
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleBody)]
return NSAttributedString(string: str, attributes: attrs)
}


//Add your image
func imageForEmptyDataSet(scrollView: UIScrollView!) -> UIImage! {
return UIImage(named: "MYIMAGE")
}


//Add your button
func buttonTitleForEmptyDataSet(scrollView: UIScrollView!, forState state: UIControlState) -> NSAttributedString! {
let str = "Add Grokkleglob"
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleCallout)]
return NSAttributedString(string: str, attributes: attrs)
}


//Add action for button
func emptyDataSetDidTapButton(scrollView: UIScrollView!) {
let ac = UIAlertController(title: "Button tapped!", message: nil, preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "Hurray", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
}

这些方法不是强制的,也可以只显示空状态而不显示按钮等。

为了 Swift 4

// MARK: - Deal with the empty data set
// Add title for empty dataset
func title(forEmptyDataSet _: UIScrollView!) -> NSAttributedString! {
let str = "Welcome"
let attrs = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)]
return NSAttributedString(string: str, attributes: attrs)
}


// Add description/subtitle on empty dataset
func description(forEmptyDataSet _: UIScrollView!) -> NSAttributedString! {
let str = "Tap the button below to add your first grokkleglob."
let attrs = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)]
return NSAttributedString(string: str, attributes: attrs)
}


// Add your image
func image(forEmptyDataSet _: UIScrollView!) -> UIImage! {
return UIImage(named: "MYIMAGE")
}


// Add your button
func buttonTitle(forEmptyDataSet _: UIScrollView!, for _: UIControlState) -> NSAttributedString! {
let str = "Add Grokkleglob"
let attrs = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: UIFontTextStyle.callout), NSAttributedStringKey.foregroundColor: UIColor.white]
return NSAttributedString(string: str, attributes: attrs)
}


// Add action for button
func emptyDataSetDidTapButton(_: UIScrollView!) {
let ac = UIAlertController(title: "Button tapped!", message: nil, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Hurray", style: .default, handler: nil))
present(ac, animated: true, completion: nil)
}

使用 background View 是可以的,但是它不像 Mail.app 那样能够很好地滚动。

我做了一些与 Xtravar类似的事情。

我在 tableViewController的视图层次结构之外添加了一个视图。 hierarchy

然后我在 tableView:numberOfRowsInSection:中使用了以下代码:

if someArray.count == 0 {
// Show Empty State View
self.tableView.addSubview(self.emptyStateView)
self.emptyStateView.center = self.view.center
self.emptyStateView.center.y -= 60 // rough calculation here
self.tableView.separatorColor = UIColor.clear
} else if self.emptyStateView.superview != nil {
// Empty State View is currently visible, but shouldn't
self.emptyStateView.removeFromSuperview()
self.tableView.separatorColor = nil
}


return someArray.count

基本上,我添加了 emptyStateView作为 tableView对象的子视图。由于分隔符会重叠视图,我将它们的颜色设置为 clearColor。 要返回到默认分隔符颜色,只需将其设置为 nil即可。

迅速的版本,但更好和更简单的形式。 * * 3.0

我希望它能达到你的目的... ..。

在您的 UITableViewController 中。

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
if filteredContacts.count > 0 {
self.tableView.backgroundView = .none;
return filteredContacts.count
} else {
Helper.EmptyMessage(message: ConstantMap.NO_CONTACT_FOUND, viewController: self)
return 0
}
} else {
if contacts.count > 0 {
self.tableView.backgroundView = .none;
return contacts.count
} else {
Helper.EmptyMessage(message: ConstantMap.NO_CONTACT_FOUND, viewController: self)
return 0
}
}
}

有功能的助手班:

 /* Description: This function generate alert dialog for empty message by passing message and
associated viewcontroller for that function
- Parameters:
- message: message that require for  empty alert message
- viewController: selected viewcontroller at that time
*/
static func EmptyMessage(message:String, viewController:UITableViewController) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: viewController.view.bounds.size.width, height: viewController.view.bounds.size.height))
messageLabel.text = message
let bubbleColor = UIColor(red: CGFloat(57)/255, green: CGFloat(81)/255, blue: CGFloat(104)/255, alpha :1)


messageLabel.textColor = bubbleColor
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .center;
messageLabel.font = UIFont(name: "TrebuchetMS", size: 18)
messageLabel.sizeToFit()


viewController.tableView.backgroundView = messageLabel;
viewController.tableView.separatorStyle = .none;
}

根据 苹果,使用容器视图控制器是正确的方法。

我将所有的空状态视图放在一个单独的情节串连板中。每个控件都有自己的 UIViewController 子类。我直接在它们的根视图下添加内容。如果需要任何操作/按钮,现在已经有一个控制器来处理它。
然后只需要从 Storyboard 实例化所需的视图控制器,将其添加为子视图控制器,并将容器视图添加到 tableView 的层次结构(子视图)中。 您的空状态视图也是可滚动的,这让您感觉良好,并允许您实现拉来刷新。

阅读 “向内容添加子视图控制器”章了解如何实现。

只要确保将子视图框架设置为 (0, 0, tableView.frame.width, tableView.frame.height)和东西将居中和对齐正确。

可能不是最好的解决方案,但是我只是在表的底部放置了一个标签,如果行 = 0,那么我就给它分配一些文本。非常简单,并且只需要几行代码就可以完成您想要完成的任务。

我的表中有两个部分(工作和学校)

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {


if (jobs.count == 0 && schools.count == 0) {
emptyLbl.text = "No jobs or schools"
} else {
emptyLbl.text = ""
}

和 Jhonston 的回答一样,但我更喜欢这个延伸:

import UIKit


extension UITableView {


func setEmptyMessage(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.font = UIFont(name: "TrebuchetMS", size: 15)
messageLabel.sizeToFit()


self.backgroundView = messageLabel
self.separatorStyle = .none
}


func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}
}

用法:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if things.count == 0 {
self.tableView.setEmptyMessage("My Message")
} else {
self.tableView.restore()
}


return things.count
}

这是最好和简单的解决方案。

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 60)];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 60)];
label.text = @"This list is empty";
label.center = self.view.center;
label.textAlignment = NSTextAlignmentCenter;
[view addSubview:label];


self.tableView.backgroundView = view;

在故事板中选择 tableviewController 场景

enter image description here

拖放 UIView 添加标签与您的信息(例如: 无数据)

enter image description here

在 TableViewController 上创建 UIView 出口(例如 yournoDataView)。

在 viewDidLoad

Background View = yourNoDataView

因此,为了更安全的解决方案:

extension UITableView {
func setEmptyMessage(_ message: String) {
guard self.numberOfRows() == 0 else {
return
}
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .center;
messageLabel.font = UIFont.systemFont(ofSize: 14.0, weight: UIFontWeightMedium)
messageLabel.sizeToFit()


self.backgroundView = messageLabel;
self.separatorStyle = .none;
}


func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}


public func numberOfRows() -> Int {
var section = 0
var rowCount = 0
while section < numberOfSections {
rowCount += numberOfRows(inSection: section)
section += 1
}
return rowCount
}
}

以及 UICollectionView:

extension UICollectionView {
func setEmptyMessage(_ message: String) {
guard self.numberOfItems() == 0 else {
return
}


let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .center;
messageLabel.font = UIFont.systemFont(ofSize: 18.0, weight: UIFontWeightSemibold)
messageLabel.sizeToFit()
self.backgroundView = messageLabel;
}


func restore() {
self.backgroundView = nil
}


public func numberOfItems() -> Int {
var section = 0
var itemsCount = 0
while section < self.numberOfSections {
itemsCount += numberOfItems(inSection: section)
section += 1
}
return itemsCount
}
}

更多通用解决方案:

    protocol EmptyMessageViewType {
mutating func setEmptyMessage(_ message: String)
mutating func restore()
}


protocol ListViewType: EmptyMessageViewType where Self: UIView {
var backgroundView: UIView? { get set }
}


extension UITableView: ListViewType {}
extension UICollectionView: ListViewType {}


extension ListViewType {
mutating func setEmptyMessage(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0,
y: 0,
width: self.bounds.size.width,
height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.font = UIFont(name: "TrebuchetMS", size: 16)
messageLabel.sizeToFit()


backgroundView = messageLabel
}


mutating func restore() {
backgroundView = nil
}
}

显示空列表的消息,无论是 UITableView还是 UICollectionView

extension UIScrollView {
func showEmptyListMessage(_ message:String) {
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: self.bounds.size.width, height: self.bounds.size.height))
let messageLabel = UILabel(frame: rect)
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.font = UIFont.systemFont(ofSize: 15)
messageLabel.sizeToFit()


if let `self` = self as? UITableView {
self.backgroundView = messageLabel
self.separatorStyle = .none
} else if let `self` = self as? UICollectionView {
self.backgroundView = messageLabel
}
}
}

用法:

if cellsViewModels.count == 0 {
self.tableView.showEmptyListMessage("No Product In List!")
}

或者:

if cellsViewModels.count == 0 {
self.collectionView?.showEmptyListMessage("No Product In List!")
}

记住: 不要忘记删除消息标签,以防数据在刷新后出现。

最简单、最快捷的方法是将标签拖到 tableView 下的侧面板上。为标签和 tableView 创建一个出口,并添加一个 if 语句以根据需要隐藏和显示标签和表。或者你也可以添加 tableView.tableFooterView = UIView (frame: CGRect.zero)这个到你的 viewDidLoad ()来给一个空表一个隐藏的感觉,如果表和背景视图有相同的颜色。

使用 Swift 4.2

  func numberOfSections(in tableView: UITableView) -> Int
{
var numOfSections: Int = 0
if self.medArray.count > 0
{
tableView.separatorStyle = .singleLine
numOfSections            = 1
tableView.backgroundView = nil
}
else
{
let noDataLabel: UILabel  = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
noDataLabel.text          = "No  Medicine available.Press + to add New Pills "
noDataLabel.textColor     = UIColor.black
noDataLabel.textAlignment = .center
tableView.backgroundView  = noDataLabel
tableView.separatorStyle  = .none
}
return numOfSections
}

我做了一些修改,这样我们就不需要手动检查计数,我还为标签添加了约束,这样不管消息有多大,都不会出错,如下所示:

extension UITableView {


fileprivate func configureLabelLayout(_ messageLabel: UILabel) {
messageLabel.translatesAutoresizingMaskIntoConstraints = false
let labelTop: CGFloat = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 25:15)
messageLabel.topAnchor.constraint(equalTo: backgroundView?.topAnchor ?? NSLayoutAnchor(), constant: labelTop).isActive = true
messageLabel.widthAnchor.constraint(equalTo: backgroundView?.widthAnchor ?? NSLayoutAnchor(), constant: -20).isActive = true
messageLabel.centerXAnchor.constraint(equalTo: backgroundView?.centerXAnchor ?? NSLayoutAnchor(), constant: 0).isActive = true
}


fileprivate func configureLabel(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
let fontSize = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 25:15)
let font: UIFont = UIFont(name: "MyriadPro-Regular", size: fontSize) ?? UIFont()
messageLabel.font = font
messageLabel.text = message
self.backgroundView = UIView()
self.backgroundView?.addSubview(messageLabel)
configureLabelLayout(messageLabel)
self.separatorStyle = .none
}


func setEmptyMessage(_ message: String, _ isEmpty: Bool) {
if isEmpty { // instead of making the check in every TableView DataSource in the project
configureLabel(message)
}
else {
restore()
}


}


func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}
}

用法

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let message: String = "The list is empty."
ticketsTableView.setEmptyMessage(message, tickets.isEmpty)
return self.tickets.count
}

或者,您也可以使用一个可定制的轻量级库

SwiftEmptyState

您可以将其添加到 Base 类中。

var messageLabel = UILabel()


func showNoDataMessage(msg: String) {
let rect = CGRect(origin: CGPoint(x: 0, y :self.view.center.y), size: CGSize(width: self.view.bounds.width - 16, height: 50.0))
messageLabel = UILabel(frame: rect)
messageLabel.center = self.view.center
messageLabel.text = msg
messageLabel.numberOfLines = 0
messageLabel.textColor = Colors.grayText
messageLabel.textAlignment = .center;
messageLabel.font = UIFont(name: "Lato-Regular", size: 17)
self.view.addSubview(messageLabel)
self.view.bringSubviewToFront(messageLabel)
}

在从 api 获取数据的类中如下所示。

func populateData(dataSource : [PRNJobDataSource]){
self.dataSource = dataSource
self.tblView.reloadData()
if self.dataSource.count == 0 {self.showNoDataMessage(msg: "No data found.")}
}

像这样藏起来。

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.dataSource.count > 0 {self.hideNoDataMessage()}
return dataSource.count
}


func hideNoDataMessage(){
messageLabel.removeFromSuperview()
}

多个数据集和部分有一个特定的用例,其中每个部分都需要一个空状态。

您可以使用这个问题的多个答案中提到的建议-提供自定义的空状态单元格。

我将尝试通过编程方式更详细地指导您完成所有步骤,希望这会有所帮助。以下是我们可以期待的结果:

enter image description here

为了简单起见,我们将处理2个数据集(2个部分) ,它们将是静态的。

我还将假设其余的 tableView 逻辑可以正确地处理数据集、 tabvleView 单元格和节。

斯威夫特,让我们做吧:

创建一个自定义空状态 UITableViewCell 类:

class EmptyTableViewCell: UITableViewCell {


override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}


required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}


let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Empty State Message"
label.font = .systemFont(ofSize: 16)
label.textColor = .gray
label.textAlignment = .left
label.numberOfLines = 1
return label
}()


private func setupView(){
contentView.addSubviews(label)
let layoutGuide = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor),
label.topAnchor.constraint(equalTo: layoutGuide.topAnchor),
label.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor),
label.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor),
label.heightAnchor.constraint(equalToConstant: 50)
])
}
}

2. 在 UITableViewController 类中添加以下内容来注册空单元格:

class TableViewController: UITableViewController {
...
let emptyCellReuseIdentifier = "emptyCellReuseIdentifier"
...
override func viewDidLoad(){
...
tableView.register(EmptyTableViewCell.self, forCellReuseIdentifier: emptyCellReuseIdentifier)
...
}
}

3. 现在让我们强调一下上面提到的一些假设:

class TableViewController: UITableViewController {
// 2 Data Sets
var firstDataSet: [String] = []
var secondDataSet: [String] = []


// Sections array
let sections: [SectionHeader] = [
.init(id: 0, title: "First Section"),
.init(id: 1, title: "Second Section")
]
...
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
sections.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].title
}
...
}


struct SectionHeader {
let id: Int
let title: String
}

现在让我们在数据源中添加一些自定义逻辑来处理我们部分中的空行。 这里,如果数据集为空,我们返回1行:

class TableViewController: UITableViewController {
...
// MARK: - Table view data source
...
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section{
case 0:
let numberOfRows = firstDataSet.isEmpty ? 1 : firstDataSet.count
return numberOfRows
case 1:
let numberOfRows = secondDataSet.isEmpty ? 1 : secondDataSet.count
return numberOfRows
default:
return 0
}
}
...
}

最后,最重要的“ cellForRowAt indexPath”:

class TableViewController: UITableViewController {
...
// MARK: - Table view data source
...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        

// Handle Empty Rows State
switch indexPath.section {
case 0:
if firstDataSet.isEmpty {
if let cell = tableView.dequeueReusableCell(withIdentifier: emptyCellReuseIdentifier) as? EmptyTableViewCell {
cell.label.text = "First Data Set Is Empty"
return cell
}
}
case 1:
if secondDataSet.isEmpty {
if let cell = tableView.dequeueReusableCell(withIdentifier: emptyCellReuseIdentifier) as? EmptyTableViewCell {
cell.label.text = "Second Data Set Is Empty"
return cell
}
}
default:
break
}
        

// Handle Existing Data Sets
if let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as? TableViewCell {
switch indexPath.section {
case 0:
...
case 1:
...
default:
break
}
return cell
}


return UITableViewCell()
}
...
}