Handling an empty UITableView. Print a friendly message
I have a UITableView that in some cases it is legal to be empty. So instead of showing the background image of the app, I would prefer to print a friendly message in the screen, such as:
This list is now empty
What is the simplest way to do it?
Solution 1:
UITableView's backgroundView property is your friend.
In viewDidLoad
or anywhere that you reloadData
you should determine if there your table is empty or not and update the UITableView's backgroundView property with a UIView containing a UILabel or just set it to nil. That's it.
It is of course possible to make UITableView's data source do double duty and return a special "list is empty" cell, it strikes me as a kludge. Suddenly numberOfRowsInSection:(NSInteger)section
has to compute the number of rows of other sections it wasn't asked about to make sure they are empty too. You also need to make a special cell that has the empty message. Also don't forget that you need to probably change the height of your cell to accommodate the empty message. This is all doable but it seems like band-aid on top of band-aid.
Solution 2:
Same as Jhonston's answer, but I preferred it as an extension:
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
}
}
Usage:
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
}
Solution 3:
Based on the answers here, here is a quick class I made that you can use on in your 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;
}
}
In your UITableViewController
you can call this in 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
}
}
With a little help from http://www.appcoda.com/pull-to-refresh-uitableview-empty/
Solution 4:
I recommend the following library: DZNEmptyDataSet
The easiest way to add it in your project is to use it with Cocaopods like so: pod 'DZNEmptyDataSet'
In your TableViewController add the following import statement (Swift):
import DZNEmptyDataSet
Then make sure your class conforms to the DNZEmptyDataSetSource
and DZNEmptyDataSetDelegate
like so:
class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate
In your viewDidLoad
add the following lines of code:
tableView.emptyDataSetSource = self
tableView.emptyDataSetDelegate = self
tableView.tableFooterView = UIView()
Now all you have to do to show the emptystate is:
//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)
}
These methods aren't mandatory, it's also possible to just show the empty state without a button etc.
For 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)
}