Si aucun résultat de la vue tableau, affiche «Aucun résultat» à l'écran

114

J'ai un tableview, où parfois il n'y a pas de résultats à lister, donc je voudrais mettre quelque chose qui dit "pas de résultats" s'il n'y a pas de résultats (soit une étiquette ou une cellule de vue de tableau?).

Existe-t-il un moyen le plus simple de le faire?

J'essaierais un labelderrière tableviewpuis cacherais l'un des deux en fonction des résultats, mais comme je travaille avec un TableViewControlleret pas un normal, ViewControllerje ne sais pas à quel point c'est intelligent ou faisable.

J'utilise également Parseet je sous-classe en tant que PFQueryTableViewController:

@interface TableViewController : PFQueryTableViewController

Je peux fournir tous les détails supplémentaires nécessaires, faites le moi savoir!

TableViewController Scène dans Storyboard:

entrez la description de l'image ici

EDIT: Par Midhun MP, voici le code que j'utilise

- (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;
}

Et voici la vue que j'obtiens, elle a toujours des lignes de séparation. J'ai le sentiment qu'il s'agit d'un petit changement, mais je ne sais pas pourquoi des lignes de séparation apparaissent?

entrez la description de l'image ici

SRMR
la source
Je pense que le truc était de configurer le footerView vide sur tableview pour se débarrasser des lignes.
Ben Sinclair
@Andy Que voulez-vous dire par là?
SRMR
N'utilisez pas la solution ajoutée à cette question. Les méthodes de source de données de vue de table ne doivent jamais faire plus que ce qu'elles sont censées faire. numberOfSectionsdevrait retourner un compte et c'est tout. Pareil pour numberOfRowsInSection. Ceux-ci peuvent être appelés plusieurs fois à tout moment. Ne mettez jamais à jour les vues, ne mettez jamais à jour les données et ne faites rien d'autre que renvoyer un décompte La logique de mise à jour des vues ne doit jamais être dans ces méthodes.
rmaddy

Réponses:

206

Vous pouvez facilement y parvenir en utilisant la backgroundViewpropriété de UITableView.

Objectif 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;
}

Rapide:

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
}

Référence Référence de classe UITableView

backgroundView Propriété

La vue d'arrière-plan de la vue de table.

Déclaration

Rapide

var backgroundView: UIView?

Objectif c

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

Discussion

La vue d'arrière-plan d'une vue de tableau est automatiquement redimensionnée pour correspondre à la taille de la vue de tableau. Cette vue est placée en tant que sous-vue de la vue tableau derrière toutes les cellules, vues d'en-tête et vues de pied de page.

Vous devez définir cette propriété sur nil pour définir la couleur d'arrière-plan de la vue tableau.

Député de Midhun
la source
Merci d'avoir répondu! Cela signifie-t-il que j'ai besoin d'une tableViewprise pour pouvoir le faire yourTableView.backgroundView? J'utilise un TableViewControllerdonc je n'ai pas de débouché pour le moment, c'est pourquoi je me demande.
SRMR
1
UITableViewControllera déjà une propriété nommée tableView, vous n'avez donc pas besoin d'en créer une
Cihan Tek
self.tableView
OK
1
Correction. Après des tests supplémentaires, j'ai dû faire en sorte que la ligne que j'ai mentionnée renvoie la même valeur. numOfSections = 1. Si je le rends à zéro et que ma vue de tableau est vide, cela fonctionne, mais lorsque je crée une cellule et que je la supprime, au lieu de supprimer et d'afficher le message, il se bloque avec l'erreur suivante. *** Fin de l'application en raison d'une exception non interceptée «NSInternalInconsistencyException», raison: «Bogue interne UITableView: impossible de générer une nouvelle carte de section avec l'ancien nombre de sections: 1 et le nouveau nombre de sections: 0». Je ne sais pas pourquoi le retour de la même valeur fonctionne, mais cela fonctionne et fonctionne comme il se doit.
ChrisOSX
2
N'utilisez pas cette solution. Les méthodes de source de données de vue de table ne doivent jamais faire plus que ce qu'elles sont censées faire. numberOfSectionsdevrait retourner un compte et c'est tout. Pareil pour numberOfRowsInSection. Ceux-ci peuvent être appelés plusieurs fois à tout moment. Ne mettez jamais à jour les vues, ne mettez jamais à jour les données et ne faites rien d'autre que renvoyer un décompte La logique de mise à jour des vues ne doit jamais être dans ces méthodes.
rmaddy
107

Pour Xcode 8.3.2 - Swift 3.1

Voici un moyen pas si connu mais incroyablement simple d'ajouter une vue "Aucun élément" à une vue de table vide qui remonte à Xcode 7. Je vous laisse le contrôle de cette logique qui ajoute / supprime le vue de la vue d'arrière-plan de la table, mais voici le flux pour et le storyboard Xcode (8.3.2):

  1. Sélectionnez la scène dans le Storyboard qui a votre vue de table.
  2. Faites glisser un UIView vide vers le "Dock des scènes" de cette scène

entrez la description de l'image ici

  1. Ajoutez un UILabel et toutes les contraintes à la nouvelle vue, puis créez un IBOutlet pour cette vue

entrez la description de l'image ici

  1. Attribuez cette vue à tableView.backgroundView

entrez la description de l'image ici

  1. Voici la magie!

entrez la description de l'image ici

En fin de compte, cela fonctionne chaque fois que vous souhaitez ajouter une vue simple à votre contrôleur de vue que vous ne voulez pas nécessairement afficher immédiatement, mais que vous ne voulez pas non plus transmettre de code.

Paul Bonneville
la source
1
Cette technique fonctionne pour tout ce pour quoi vous avez besoin d'une vue simple. Je l'ai également utilisé pour les en-têtes de section de tableau personnalisés. Vraiment, vous pouvez l'utiliser pour tout ce pour quoi vous avez besoin d'une vue. Faites simplement référence via un IBOutlet et ajoutez-le en tant que sous-vue partout où vous avez besoin d'une vue personnalisée.
Paul Bonneville
Comme @gmogames l'a dit, après avoir fait IOS pendant 2 ans, c'est la première fois que vous connaissez l'arrière-plan de tableView!. thanks man
Ali Adil
1
Merci pour cela. Juste pour le mettre là-bas, j'ai dû utiliser tableView.separatorStyle = .none et redimensionner l'UIView par défaut pour voir l'arrière-plan. J'utilise Xcode 9.4 avec Swift 4.
Hammad Tariq
Comment placez-vous la vue personnalisée?
Nol4635
1
@ Nol4635 Dans le cas de l'arrière-plan de la vue de table, vous affectez simplement la vue que vous avez créée en tant qu'IBOutlet à l'arrière-plan de la vue de la table ou, si vous souhaitez simplement ajouter la vue à une autre vue, vous pouvez simplement l'ajouter en tant que sous-vue. Vous devrez définir les valeurs du cadre, mais c'est comme n'importe quelle autre vue.
Paul Bonneville
30

Version Swift du code ci-dessus: -

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
}

Mais si vous chargez des informations à partir d'un JSON, vous devez vérifier si le JSON est vide ou non, par conséquent, si vous mettez un code comme celui-ci, il affiche initialement le message "Aucune donnée" puis disparaît. Parce qu'après le rechargement de la table, le message se cache. Vous pouvez donc placer ce code à l'endroit où charger les données JSON dans un tableau. ALORS :-

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
    })
}
Chathuranga Silva
la source
Cela fonctionne très bien pour afficher l'étiquette lorsqu'aucune donnée n'est dans les lignes. Cependant, quelle est la meilleure façon de gérer le moment où les données arrivent et self.tableView.reloadData()sont appelées? J'ai constaté qu'il ajoutait mes nouvelles lignes, mais le noDataLabelest toujours affiché en dessous.
tylerSF
1
Si votre tableau a un élément juste défini self.tableView.backgroundView = nil
Chathuranga Silva
if jobs_list.count == 0 {// ajouter le code donné ci-dessus} else {self.tableView.backgroundView = nil}
Chathuranga Silva
parfait. l'ajout self.tableView.backgroundView = nilde ma déclaration if avant return jobs_list.count fonctionne bien. merci
tylerSF
N'utilisez pas la première solution dans cette réponse. Les méthodes de source de données de vue de table ne doivent jamais faire plus que ce qu'elles sont censées faire. numberOfSectionsdevrait retourner un compte et c'est tout. Pareil pour numberOfRowsInSection. Ceux-ci peuvent être appelés plusieurs fois à tout moment. Ne mettez jamais à jour les vues, ne mettez jamais à jour les données et ne faites rien d'autre que renvoyer un décompte La logique de mise à jour des vues ne doit jamais être dans ces méthodes.
rmaddy
6

Vous pouvez essayer ce contrôle. C'est assez soigné. DZNEmptyDataSet

Ou si j'étais toi, tout ce que je ferais c'est

  • Vérifiez si votre tableau de données est vide
  • S'il est vide, ajoutez-y un objet appelé @ "No Data"
  • Afficher cette chaîne dans cell.textLabel.text

Peasy facile

Sam B
la source
5

Swift 3 (mis à jour):

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
la source
N'utilisez pas cette solution. Les méthodes de source de données de vue de table ne doivent jamais faire plus que ce qu'elles sont censées faire. numberOfSectionsdevrait retourner un compte et c'est tout. Pareil pour numberOfRowsInSection. Ceux-ci peuvent être appelés plusieurs fois à tout moment. Ne mettez jamais à jour les vues, ne mettez jamais à jour les données et ne faites rien d'autre que renvoyer un décompte La logique de mise à jour des vues ne doit jamais être dans ces méthodes.
rmaddy
Je suis d'accord. Cette solution peut être améliorée, mais elle peut également être utilisée.
Teodor Ciuraru
Comment pouvez-vous convenir que vous ne devriez pas utiliser cette solution, mais indiquer ensuite qu'elle peut également être utilisée? C'est une contradiction.
rmaddy
Je conviens que cette solution peut être améliorée, mais, dans son état actuel, fonctionnera également.
Teodor Ciuraru
5

Swift3.0

J'espère qu'il servira votre objectif ...... Dans votre 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
        }
    }
}

Classe d'assistance avec fonction:

 /* 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
la source
N'utilisez pas cette solution. Les méthodes de source de données de vue de table ne doivent jamais faire plus que ce qu'elles sont censées faire. numberOfSectionsdevrait retourner un compte et c'est tout. Pareil pour numberOfRowsInSection. Ceux-ci peuvent être appelés plusieurs fois à tout moment. Ne mettez jamais à jour les vues, ne mettez jamais à jour les données et ne faites rien d'autre que renvoyer un décompte La logique de mise à jour des vues ne doit jamais être dans ces méthodes.
rmaddy
3

Je pense que le moyen le plus élégant de résoudre votre problème est de passer d' UITableViewControllerunUIViewController contenant un UITableView. De cette façon, vous pouvez ajouter ce que UIViewvous voulez en tant que sous-vues de la vue principale.

Je ne recommanderais pas d'utiliser un UITableViewCell pour ce faire, vous devrez peut-être ajouter des éléments supplémentaires à l'avenir et les choses peuvent rapidement devenir laides.

Vous pouvez également faire quelque chose comme ça, mais ce n'est pas non plus la meilleure solution.

UIWindow* window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: OverlayView];
Cihan Tek
la source
Merci pour la couleur autour de laquelle est la manière la plus élégante de mettre en œuvre, toujours une bonne façon d'y penser. Je pourrais essayer de changer mon storyboard pour utiliser un UIViewControllercontenant un UITableView.
SRMR
1
C'est le moyen le plus évolutif. Je n'ai jamais utilisé de UITableViewControllermoi - même, car l'utiliser n'est pas vraiment beaucoup plus facile que de le faire dans l'autre sens, mais cela limite votre capacité à changer les choses à l'avenir.
Cihan Tek
Ce n'est pas à l'épreuve du temps si vous avez des barres translucides. Je déconseille vivement. À tout le moins, créez un UIViewController qui contient un UITableViewController et ajuste les choses de manière appropriée.
xtravar
3

Utilisez ce code dans votre numberOfSectionsInTableViewméthode: -

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
la source
1
N'utilisez pas cette solution. Les méthodes de source de données de vue de table ne doivent jamais faire plus que ce qu'elles sont censées faire. numberOfSectionsdevrait retourner un compte et c'est tout. Pareil pour numberOfRowsInSection. Ceux-ci peuvent être appelés plusieurs fois à tout moment. Ne mettez jamais à jour les vues, ne mettez jamais à jour les données et ne faites rien d'autre que renvoyer un décompte La logique de mise à jour des vues ne doit jamais être dans ces méthodes.
rmaddy
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
la source
2

Si vous n'utilisez pas le pied de page tableview et que vous ne voulez pas que la tableview remplisse l'écran avec des cellules de tableau par défaut vides, je vous suggère de définir votre pied de page tableview sur un UIView vide. Je ne connais pas la syntaxe correcte pour faire cela dans obj-c ou Swift, mais dans Xamarin.iOS, je le ferais comme ceci:

public class ViewController : UIViewController
{
    UITableView _table;

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

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

        _table.TableFooterView = new UIView();
    }
}

Le code ci-dessus entraînera une vue de table sans les cellules vides

frmi
la source
2

Voici la solution qui a fonctionné pour moi.

  1. Ajoutez le code suivant à un nouveau fichier.

  2. Remplacez votre classe de table par la classe personnalisée "MyTableView" à partir du storyboard ou du .xib

(cela ne fonctionnera que pour la première section. Si vous souhaitez personnaliser davantage, apportez les modifications nécessaires à la fonction reloadData () MyTableView pour les autres 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
la source
C'est une bonne approche. J'ai ajouté quelques améliorations comme suit pour le rendre plus complet. 1. Mettez en cache le style de séparateur de tableview et restaurez-le lors d'une transition d'état vide / non vide pour rendre cette sous-classe applicable aux cas d'utilisation dans lesquels différents styles de séparateur sont souhaités. 2. Ajoutez @IBInspectable var emptyStateText: String?et marquez cette classe comme @IBDesignablevous pourrez alors changer le texte d'état vide différent pour différentes scènes dans le storyboard ou les xibs. Prendre plaisir! :)
Egist Li
1

Je présenterais une vue de superposition qui a l'apparence et le message que vous voulez si la vue de la table n'a pas de résultats. Vous pouvez le faire dans ViewDidAppear, vous avez donc les résultats avant d'afficher / de ne pas afficher la vue.

Siriss
la source
1

Si vous voulez faire cela sans aucun code, essayez ceci!

Cliquez sur votre tableView.

tableauView Capture d'écran

Changez le style de "simple" à "groupé".

Capture d'écran des propriétés de la vue tableau-2

Maintenant, lorsque vous utilisez ...

tableView.backgroundView = INSERER VOTRE ETIQUETTE OU VUE

Il ne montrera pas les séparateurs!

F. Morales
la source
1

Ajoutez ce code dans un fichier et changez votre type de collection en 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
la source