118

I have a tableview, where sometimes there might not be any results to list, so I would like to put something up that says "no results" if there are no results (either a label or one table view cell?).

Is there an easiest way to do this?

I would try a label behind the tableview then hide one of the two based on the results, but since I'm working with a TableViewController and not a normal ViewController I'm not sure how smart or doable that is.

I'm also using Parse and subclassing as a PFQueryTableViewController:

@interface TableViewController : PFQueryTableViewController

I can provide any additional details needed, just let me know!

TableViewController Scene in Storyboard:

enter image description here

EDIT: Per Midhun MP, here's the code I'm using

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSInteger numOfSections = 0;
    if ([self.stringArray count] > 0)
    {
        self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
        numOfSections                 = 1;
        //yourTableView.backgroundView   = nil;
        self.tableView.backgroundView = nil;
    }
    else
    {
        UILabel *noDataLabel         = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height)];
        noDataLabel.text             = @"No data available";
        noDataLabel.textColor        = [UIColor blackColor];
        noDataLabel.textAlignment    = NSTextAlignmentCenter;
        //yourTableView.backgroundView = noDataLabel;
        //yourTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        self.tableView.backgroundView = noDataLabel;
        self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }

    return numOfSections;
}

And here's the View I'm getting, it still has separator lines. I get the feeling that this is some small change, but I'm not sure why separator lines are showing up?

enter image description here

SRMR
  • 3,064
  • 6
  • 31
  • 59
  • I think the trick was to setup empty footerView on tableview to get rid of lines. – pronebird Mar 11 '15 at 00:12
  • @Andy What do you mean by that? – SRMR Mar 11 '15 at 22:41
  • 1
    Do not use the solution added to this question. Table view data source methods must never do more than they are supposed to do. `numberOfSections` should return a count and that is it. Same for `numberOfRowsInSection`. These can be called many times at any time. Never update views or update data or do anything except return a count. The logic for updating views must never be in these methods. – rmaddy May 30 '18 at 02:23

15 Answers15

216

You can easily achieve that by using backgroundView property of UITableView.

Objective C:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSInteger numOfSections = 0;
    if (youHaveData)
    {
        yourTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
        numOfSections                = 1;
        yourTableView.backgroundView = nil;
    }
    else
    {   
        UILabel *noDataLabel         = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, yourTableView.bounds.size.width, yourTableView.bounds.size.height)];
        noDataLabel.text             = @"No data available";
        noDataLabel.textColor        = [UIColor blackColor];
        noDataLabel.textAlignment    = NSTextAlignmentCenter;
        yourTableView.backgroundView = noDataLabel;
        yourTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }

    return numOfSections;
}

Swift:

func numberOfSections(in tableView: UITableView) -> Int
{
    var numOfSections: Int = 0
    if youHaveData
    {
        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 data available"
        noDataLabel.textColor     = UIColor.black
        noDataLabel.textAlignment = .center
        tableView.backgroundView  = noDataLabel
        tableView.separatorStyle  = .none
    }
    return numOfSections
}

Reference UITableView Class Reference

backgroundView Property

The background view of the table view.

Declaration

Swift

var backgroundView: UIView?

Objective-C

@property(nonatomic, readwrite, retain) UIView *backgroundView

Discussion

A table view’s background view is automatically resized to match the size of the table view. This view is placed as a subview of the table view behind all cells, header views, and footer views.

You must set this property to nil to set the background color of the table view.

Community
  • 1
  • 1
Midhun MP
  • 103,496
  • 31
  • 153
  • 200
  • Thanks for the answer! Does this mean I need a `tableView` outlet so that I can do `yourTableView.backgroundView`? I'm using a `TableViewController` so I don't have an outlet for it right now, thats why I'm wondering. – SRMR Feb 16 '15 at 22:05
  • 1
    `UITableViewController` already has a property named `tableView` so you don't need to create one – Cihan Tek Feb 16 '15 at 22:11
  • Got it, using `self.tableView` – SRMR Feb 16 '15 at 22:18
  • I'm still getting separator lines, any idea why? Thanks! – SRMR Feb 16 '15 at 22:25
  • @SRMR: Did you set `UITableViewCellSeparatorStyleNone` as seperatorStyle ? – Midhun MP Feb 16 '15 at 22:43
  • Yeah, I edited my answer to show the code I have now and screenshot. See anything funky that I'm missing? – SRMR Feb 16 '15 at 23:24
  • @SRMR: I checked the code by creating a new project (using Master-Detail template provided in XCode). And it is working perfectly. Probably you are having some settings in storyboard, that is causing the issue – Midhun MP Feb 17 '15 at 00:49
  • For some reason, I am unable to set the backgroundView or backgroundColor of my UITableView and have it show up. I have tried everything that can be tried with code, thinking it's a Storyboard issue. – avance Sep 08 '16 at 22:31
  • Should we not instantiate `UILabel` class once at `viewDidAppear` when the table view is measured on the screen? – dobridog Sep 14 '16 at 21:12
  • Working perfect. However, when deleting a row I was getting an error. In the else portion I set numOfSections = 0. Solved my crashing error. – ChrisOSX Mar 12 '17 at 22:05
  • This code will not work like this it will crash when return have number of rows less than 1. I will edit the code. – Kegham K. Mar 14 '17 at 21:32
  • @KeghamK.That crash is not happened by this code. If there is no row available after a deletion, you should use reloadData, instead of deleterowatindexpath. – Midhun MP Mar 15 '17 at 05:08
  • @ChrisOSX: I'm wondered why that helped you. Because we are initialised the numOfSections to 0 and if it won't enter to the if block the value will be 0. So again setting it to 0 in else won't make any difference. I think you should check my previous comment, I assume that is the reason for your crash. Anyway thanks for your comment. – Midhun MP Mar 15 '17 at 05:10
  • 1
    Correction. Upon further testing, I actually had to make that line I mentioned return the same value. numOfSections = 1. If I make it zero and my table view is empty, it works, but when I create a cell and then delete it, instead of deleting and showing the message, it crashes with the following error. *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView internal bug: unable to generate a new section map with old section count: 1 and new section count: 0'. I don't know why returning the same value works, but it does and works how it should. – ChrisOSX Mar 15 '17 at 14:44
  • 4
    Do not use this solution. Table view data source methods must never do more than they are supposed to do. `numberOfSections` should return a count and that is it. Same for `numberOfRowsInSection`. These can be called many times at any time. Never update views or update data or do anything except return a count. The logic for updating views must never be in these methods. – rmaddy May 30 '18 at 02:20
  • @rmaddy Then can you please answer the question ? Also could you please share the official document which mentions the above mentioned points ? Could you please let me know the scenarios in which the `numberOfSections` can be called multiple times (Other than calling reload data)? – Midhun MP May 30 '18 at 05:59
  • @rmaddy which is the prettier way? using backgroundView or subclassing a cell class for showing no data? – eemrah Nov 08 '18 at 11:42
111

For Xcode 8.3.2 - Swift 3.1

Here is a not-so-well-known but incredibly easy way to achieve adding a "No Items" view to an empty table view that goes back to Xcode 7. I'll leave it to you control that logic that adds/removes the view to the table's background view, but here is the flow for and Xcode (8.3.2) storyboard:

  1. Select the scene in the Storyboard that has your table view.
  2. Drag an empty UIView to the "Scene Dock" of that scene

enter image description here

  1. Add a UILabel and any constraints to the new view and then create an IBOutlet for that view

enter image description here

  1. Assign that view to the tableView.backgroundView

enter image description here

  1. Behold the magic!

enter image description here

Ultimately this works anytime you want to add a simple view to your view controller that you don't necessarily want to be displayed immediately, but that you also don't want to hand code.

Paul Bonneville
  • 1,905
  • 2
  • 13
  • 17
  • 1
    This technique works for anything that you need a simple view for. I've also used it for custom table section headers. Really, you can use it for anything that you need a view for. Just reference via an IBOutlet and add it as subview anywhere you need a custom view. – Paul Bonneville Jul 18 '17 at 22:46
  • As @gmogames said, after doing IOS for 2 years this is the first time know about tableView background !. thanks man – Mohammed Riyadh Dec 26 '17 at 19:05
  • 1
    Thanks for this. Just to put it out there, I had to use tableView.separatorStyle = .none and resize the default UIView to see the backgroundView. I am using Xcode 9.4 with Swift 4. – Hammad Tariq Jun 15 '18 at 12:26
  • How do you place the custom view? – Nol4635 Jan 02 '19 at 17:44
  • 1
    @Nol4635 In the case of the tableview background, you just assign the view you created as an IBOutlet to the tableview's background or, if you just want to add the view to any another view, you can just add it as a subview. You'll have to set the frame values, but it is like any other view. – Paul Bonneville Jan 02 '19 at 21:06
30

Swift Version of above code :-

func numberOfSectionsInTableView(tableView: UITableView) -> Int {

    var numOfSection: NSInteger = 0

    if CCompanyLogoImage.count > 0 {

        self.tableView.backgroundView = nil
        numOfSection = 1


    } else {

        var noDataLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
        noDataLabel.text = "No Data Available"
        noDataLabel.textColor = UIColor(red: 22.0/255.0, green: 106.0/255.0, blue: 176.0/255.0, alpha: 1.0)
        noDataLabel.textAlignment = NSTextAlignment.Center
        self.tableView.backgroundView = noDataLabel

    }
    return numOfSection
}

But If you are loading Information From a JSON , you need to check whether the JSON is empty or not , therefor if you put code like this it initially shows "No data" Message then disappear. Because after the table reload data the message hide. So You can put this code where load JSON data to an array. SO :-

func numberOfSectionsInTableView(tableView: UITableView) -> Int {

    return 1
}

func extract_json(data:NSData) {


    var error: NSError?

    let jsonData: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers , error: &error)
    if (error == nil) {
        if let jobs_list = jsonData as? NSArray
        {
            if jobs_list.count == 0 {

                var noDataLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
                noDataLabel.text = "No Jobs Available"
                noDataLabel.textColor = UIColor(red: 22.0/255.0, green: 106.0/255.0, blue: 176.0/255.0, alpha: 1.0)
                noDataLabel.textAlignment = NSTextAlignment.Center
                self.tableView.backgroundView = noDataLabel

            }

            for (var i = 0; i < jobs_list.count ; i++ )
            {
                if let jobs_obj = jobs_list[i] as? NSDictionary
                {
                    if let vacancy_title = jobs_obj["VacancyTitle"] as? String
                    {
                        CJobTitle.append(vacancy_title)

                        if let vacancy_job_type = jobs_obj["VacancyJobType"] as? String
                        {
                            CJobType.append(vacancy_job_type)

                            if let company_name = jobs_obj["EmployerCompanyName"] as? String
                            {
                                CCompany.append(company_name)

                                    if let company_logo_url = jobs_obj["EmployerCompanyLogo"] as? String
                                    {
                                        //CCompanyLogo.append("http://google.com" + company_logo_url)

                                        let url = NSURL(string: "http://google.com" + company_logo_url )
                                        let data = NSData(contentsOfURL:url!)
                                        if data != nil {
                                            CCompanyLogoImage.append(UIImage(data: data!)!)
                                        }

                                        if let vacancy_id = jobs_obj["VacancyID"] as? String
                                        {
                                            CVacancyId.append(vacancy_id)

                                        }

                                    }

                            }

                        }
                    }
                }
            }
        }
    }
    do_table_refresh();


}

func do_table_refresh() {

    dispatch_async(dispatch_get_main_queue(), {
        self.tableView.reloadData()
        return
    })
}
Mudith Chathuranga Silva
  • 7,253
  • 2
  • 50
  • 58
  • This works very well for displaying the label when no data is in the rows. However, what is the best way to handle when data does come in and `self.tableView.reloadData()` is called? I've found that it is adding my new rows, but the `noDataLabel` is still displayed underneath them. – tylerSF Feb 19 '16 at 15:46
  • 1
    If you array has item just set self.tableView.backgroundView = nil – Mudith Chathuranga Silva Feb 19 '16 at 16:18
  • if jobs_list.count == 0 { // add above given code } else { self.tableView.backgroundView = nil } – Mudith Chathuranga Silva Feb 19 '16 at 16:22
  • perfect. adding `self.tableView.backgroundView = nil` in my if statement before `return jobs_list.count` works well. thanks – tylerSF Feb 20 '16 at 18:19
  • Do not use the first solution in this answer. Table view data source methods must never do more than they are supposed to do. `numberOfSections` should return a count and that is it. Same for `numberOfRowsInSection`. These can be called many times at any time. Never update views or update data or do anything except return a count. The logic for updating views must never be in these methods. – rmaddy May 30 '18 at 02:21
7

You can try this control. Its is pretty neat. DZNEmptyDataSet

Or if I were you all I would do is

  • Check to see if your data array is empty
  • If it is empty then add one object called @"No Data" to it
  • Display that string in cell.textLabel.text

Easy peasy

Sam B
  • 27,273
  • 15
  • 84
  • 121
6

Swift3.0

I hope it server your purpose...... In your 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
        }
    }
}

Helper Class with function :

 /* 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;
        }
Ravindra Shekhawat
  • 4,275
  • 1
  • 19
  • 26
  • Do not use this solution. Table view data source methods must never do more than they are supposed to do. `numberOfSections` should return a count and that is it. Same for `numberOfRowsInSection`. These can be called many times at any time. Never update views or update data or do anything except return a count. The logic for updating views must never be in these methods. – rmaddy May 30 '18 at 02:22
6

Swift 3 (updated):

override func numberOfSections(in tableView: UITableView) -> Int {
    if myArray.count > 0 {
        self.tableView.backgroundView = nil
        self.tableView.separatorStyle = .singleLine
        return 1
    }

    let rect = CGRect(x: 0,
                      y: 0,
                      width: self.tableView.bounds.size.width,
                      height: self.tableView.bounds.size.height)
    let noDataLabel: UILabel = UILabel(frame: rect)

    noDataLabel.text = "Custom message."
    noDataLabel.textColor = UIColor.white
    noDataLabel.textAlignment = NSTextAlignment.center
    self.tableView.backgroundView = noDataLabel
    self.tableView.separatorStyle = .none

    return 0
}
Teodor Ciuraru
  • 3,417
  • 1
  • 32
  • 39
  • Do not use this solution. Table view data source methods must never do more than they are supposed to do. `numberOfSections` should return a count and that is it. Same for `numberOfRowsInSection`. These can be called many times at any time. Never update views or update data or do anything except return a count. The logic for updating views must never be in these methods. – rmaddy May 30 '18 at 02:21
  • I agree. This solution can be improved, but it also can be used. – Teodor Ciuraru May 30 '18 at 08:03
  • How can you agree that you should not use this solution but then state that is can also be used? That's a contradiction. – rmaddy May 30 '18 at 13:30
  • I agree that this solution can be improved, but, in its current state, will also work. – Teodor Ciuraru May 30 '18 at 13:30
3

I think the most elegant way to solve your problem is switching from a UITableViewController to a UIViewController that contains a UITableView. This way you can add whatever UIView you want as subviews of the main view.

I wouldn't recommend using a UITableViewCell to do this you might need to add additional things in the future and things can quicky get ugly.

You can also do something like this, but this isn't the best solution either.

UIWindow* window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: OverlayView];
Cihan Tek
  • 5,349
  • 3
  • 22
  • 29
  • Thanks for the color around which is most elegant way to implement, always a good way to think about it. I might try changing my storyboard to use a `UIViewController` containing a `UITableView`. – SRMR Feb 16 '15 at 22:08
  • 1
    That's the most future-proof way. I have never used a `UITableViewController` myself, because using it isn't really much easier than doing it the other way, yet it limits your ability to change things in the future. – Cihan Tek Feb 16 '15 at 22:14
  • This is not future-proof if you have translucent bars. I highly recommend against. At the very least, make a UIViewController that contains a UITableViewController and adjusts things appropriately. – xtravar Jul 13 '16 at 23:54
3

Use this code in Your numberOfSectionsInTableView method:-

if ([array count]==0
{

    UILabel *fromLabel = [[UILabel alloc]initWithFrame:CGRectMake(50, self.view.frame.size.height/2, 300, 60)];                                                                                        
    fromLabel.text =@"No Result";
    fromLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
    fromLabel.backgroundColor = [UIColor clearColor];
    fromLabel.textColor = [UIColor lightGrayColor];
    fromLabel.textAlignment = NSTextAlignmentLeft;
    [fromLabel setFont:[UIFont fontWithName:Embrima size:30.0f]];
    [self.view addSubview:fromLabel];
    [self.tblView setHidden:YES];
}
Chandan kumar
  • 1,074
  • 12
  • 31
  • 1
    Do not use this solution. Table view data source methods must never do more than they are supposed to do. `numberOfSections` should return a count and that is it. Same for `numberOfRowsInSection`. These can be called many times at any time. Never update views or update data or do anything except return a count. The logic for updating views must never be in these methods. – rmaddy May 30 '18 at 02:22
2

I would present a an overlay view that has the look and message you want if the tableview has no results. You could do it in ViewDidAppear, so you have the results before showing/not showing the view.

Siriss
  • 3,737
  • 4
  • 32
  • 65
2

SWIFT 3

        let noDataLabel: UILabel     = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
        noDataLabel.text             = "No data available"
        noDataLabel.textColor        = UIColor.white
        noDataLabel.font             = UIFont(name: "Open Sans", size: 15)
        noDataLabel.textAlignment    = .center
        tableView.backgroundView = noDataLabel
        tableView.separatorStyle = .none
Ahmed Safadi
  • 4,402
  • 37
  • 33
2

Here is the solution that worked for me.

  1. Add the following code to a new file.

  2. Change your table class to the custom class "MyTableView" from storyboard or .xib

(this will work for the first section only. If you want to customize more, do changes in the MyTableView reloadData() function accordingly for other sections)

public class MyTableView: UITableView {

    override public func reloadData() {
        super.reloadData()

        if self.numberOfRows(inSection: 0) == 0 {
            if self.viewWithTag(1111) == nil {
                let noDataLabel = UILabel()
                noDataLabel.textAlignment = .center
                noDataLabel.text = "No Data Available"
                noDataLabel.tag = 1111
                noDataLabel.center = self.center
                self.backgroundView = noDataLabel
            }

        } else {
            if self.viewWithTag(1111) != nil {
                self.backgroundView = nil
            }
        }
    }
}
ShrikantWalekar
  • 145
  • 1
  • 6
  • 1
    This is a good approach. I've added some enhancement as following to make it more well rounded. 1. Cache separator style of tableview and restore it upon empty/non-empty state transition to make this subclass applicable for use cases which different separator styles are desired. 2. Add `@IBInspectable var emptyStateText: String?` and mark this class as `@IBDesignable` then you will be able to change different empty state text for different scenes in storyboard or xibs. Enjoy! :) – Egist Li May 09 '19 at 12:46
  • This code assumes there is only one section in the table view. – HangarRash Jun 01 '23 at 15:05
2

If you don't use the tableview footer and do not want the tableview to fill up the screen with empty default table cells i would suggest that you set your tableview footer to an empty UIView. I do not know the correct syntax for doing this in obj-c or Swift, but in Xamarin.iOS i would do it like this:

public class ViewController : UIViewController
{
    UITableView _table;

    public ViewController (IntPtr handle) : base (handle)
    {
    }

    public override void ViewWillAppear(bool animated) {
        // Initialize table

        _table.TableFooterView = new UIView();
    }
}

Above code will result in a tableview without the empty cells

frmi
  • 508
  • 1
  • 7
  • 21
1

Add this code in one file and change your collection type to CustomCollectionView

import Foundation

class CustomCollectionView: UICollectionView {

  var emptyModel = EmptyMessageModel()
  var emptyView: EmptyMessageView?
  var showEmptyView: Bool = true

  override func reloadData() {
    super.reloadData()

    emptyView?.removeFromSuperview()
    self.backgroundView = nil

    if !showEmptyView {
      return
    }

    if numberOfSections < 1 {
      let rect = CGRect(x: 0,
                        y: 0,
                        width: self.bounds.size.width,
                        height: self.bounds.size.height)

      emptyView = EmptyMessageView()
      emptyView?.frame = rect
      if let emptyView = emptyView {
        //                self.addSubview(emptyView)
        self.backgroundView = emptyView
      }
      emptyView?.setView(with: emptyModel)

    } else {
      emptyView?.removeFromSuperview()
      self.backgroundView = nil
    }
  }
}

class EmptyMessageView: UIView {

  @IBOutlet weak var messageLabel: UILabel!
  @IBOutlet weak var imageView: UIImageView!

  var view: UIView!

  override init(frame: CGRect) {
    super.init(frame: frame)
    xibSetup()
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    xibSetup()
  }

  func xibSetup() {
    view = loadViewFromNib()

    view.frame = bounds

    view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    addSubview(view)
  }

  func loadViewFromNib() -> UIView {

    let bundle = Bundle(for: type(of: self))
    let nib = UINib(nibName: "EmptyMessageView", bundle: bundle)
    let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView

    return view
  }

  func setView(with model: EmptyMessageModel) {
    messageLabel.text = model.message ?? ""
    imageView.image = model.image ?? #imageLiteral(resourceName: "no_notification")
  }
}

///////////
class EmptyMessageModel {
  var message: String?
  var image: UIImage?

  init(message: String = "No data available", image: UIImage = #imageLiteral(resourceName: "no_notification")) {
    self.message = message
    self.image = image
  }
}
ShrikantWalekar
  • 145
  • 1
  • 6
1

If you want to do this without any code, try this!

Click on your tableView.

tableView Screenshot

Change the style from "plain" to "grouped".

Table View properties screenshot-2

Now when you use ....

tableView.backgroundView = INSERT YOUR LABEL OR VIEW

It will not show the separators!

Mark Stewart
  • 2,046
  • 4
  • 22
  • 32
F. Morales
  • 437
  • 6
  • 5
0

Many answers here require you to add ui code to numberOfSections TableView extension, which you should really avoid doing. Instead, here is a Swift 5 solution that works just as well.

  1. Add a Class variable let noDataLabel = UILabel() and add it as a subview to your View:

     noDataLabel.text = "no data string"
     noDataLabel.textColor = .white
     noDataLabel.numberOfLines = 0
     noDataLabel.textAlignment = .center
     noDataLabel.isHidden = true
     noDataLabel.layer.zPosition = 2
     noDataLabel.translatesAutoresizingMaskIntoConstraints = false
     view.addSubview(noDataLabel)
    

Just to be sure, you can set its zPosition to be one above your TableView. Your label should be set to isHidden = true. Don't forget to set translateAutoresizingMaskIntoConstraints to false and add the necessary constraints:

NSLayoutConstraint.activate([
    //Fill Screen
    tableView.widthAnchor.constraint(equalTo: view.widthAnchor),
    tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    tableView.heightAnchor.constraint(equalTo: view.heightAnchor),
    tableView.topAnchor.constraint(equalTo: view.topAnchor),
    //Center
    noDataLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    noDataLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    noDataLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
])

That's pretty much it! Now, inside of your update() function (called when you refresh your tableView for example, or whenever you try to load data), add the following lines of code:

        self.tableView.reloadData()
        if dataArray.isEmpty {
            noDataLabel.isHidden = false
        }else{
            noDataLabel.isHidden = true
        }
        refreshControl.endRefreshing()

Be sure to add your if else just below your tableView reloadData function.

Jan L
  • 233
  • 1
  • 10