Gestion d'un UITableView vide. Imprimez un message convivial

124

J'ai un UITableView qui dans certains cas, il est légal d'être vide. Donc, au lieu d'afficher l'image d'arrière-plan de l'application, je préférerais imprimer un message convivial à l'écran, tel que:

Cette liste est maintenant vide

Quelle est la manière la plus simple de le faire?

cateof
la source
10
Découvrez ce projet: github.com/dzenbot/DZNEmptyDataSet
oleynikd
@oleynikd Merci !!!!!
Luda

Réponses:

173

La propriété backgroundView de UITableView est votre ami.

Dans viewDidLoadou n'importe où, vous reloadDatadevez déterminer si votre table est vide ou non et mettre à jour la propriété backgroundView de l'UITableView avec un UIView contenant un UILabel ou simplement le définir sur nil. C'est tout.

Il est bien sûr possible de faire en sorte que la source de données d'UITableView fasse une double tâche et retourne une cellule spéciale "la liste est vide", cela me semble être un kludge. Il faut soudainement numberOfRowsInSection:(NSInteger)sectioncalculer le nombre de lignes des autres sections sur lesquelles il n'a pas été demandé pour s'assurer qu'elles sont également vides. Vous devez également créer une cellule spéciale contenant le message vide. N'oubliez pas non plus que vous devez probablement changer la hauteur de votre cellule pour accueillir le message vide. Tout cela est faisable, mais cela semble être un pansement en plus du pansement.

Ray Fix
la source
12
Le backgroundView est la meilleure solution, je pense. Je vous remercie!
Ferran Maylinch
1
Un inconvénient est que si la vue de table est abaissée, le message reste sur sa position. J'aimerais plutôt avoir comme dans l'application App Store sous les mises à jour. Ici, le message vide va avec le comportement de défilement ...
test du
@testing évidemment si vous avez besoin de la messagerie vide pour faire défiler, vous ne pouvez pas utiliser la vue d'arrière-plan car cela ne fait pas partie de la hiérarchie de défilement. Vous souhaitez probablement utiliser une section personnalisée et UITableViewCell pour l'état vide.
LightningStryk
Basculer la backgroundViewpropriété cachée est la voie à suivre. Avec DZNEmptyDataSet, nous devons utiliser sonemptyDataSetSource
onmyway133
Si vous souhaitez ajouter des boutons, vous devez faire: tableView.backgroundView!.userInteraction = trueaprès la ligne que vous définissez tableView.backgroundView = constructMyViewWithButtons()ou comme vous le définissez.
kbpontius
90

Identique à la réponse de Jhonston, mais je l'ai préférée comme 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
}
Frankie
la source
Merci pour votre suggestion.
ssowri1
1
L'extension c'est mieux! Merci.
Ogulcan Orhan
J'adore l'extension :)
Alix
1
Si vous avez des sections, vous devrez déplacer cette logique vers la func numberOfSections(in tableView: UITableView) -> Intméthode
créer
87

Sur la base des réponses ici, voici un cours rapide que j'ai créé que vous pouvez utiliser dans votre 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;
    }
}

Dans votre, UITableViewControllervous pouvez appeler celanumberOfSectionsInTableView(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
    }
}

entrez la description de l'image ici

Avec un peu d'aide de http://www.appcoda.com/pull-to-refresh-uitableview-empty/

Johnston
la source
aimerait également y ajouter une image. Mais une excellente solution rapide.
AnBisw
10
utilisez une extension au lieu d'une classe (ne passez pas le contrôleur de vue)
Cesare
Je n'ai pas réussi à faire fonctionner le code ci-dessus pour moi, mais j'ai pu faire fonctionner le code de cette réponse (la première), au cas où d'autres auraient le même problème. Je pense également que la deuxième réponse fonctionnerait également (en utilisant le dock de scène) - stackoverflow.com/questions/28532926/…
Renee Olson
viewController.tableView.separatorStyle = .nonen'est pas nécessaire.
Dmitry
17

Je recommande la bibliothèque suivante: DZNEmptyDataSet

Le moyen le plus simple de l'ajouter dans votre projet est de l'utiliser avec des Cocaopods comme ceci: pod 'DZNEmptyDataSet'

Dans votre TableViewController, ajoutez l'instruction d'importation suivante (Swift):

import DZNEmptyDataSet

Ensuite, assurez-vous que votre classe est conforme à DNZEmptyDataSetSourceet DZNEmptyDataSetDelegateainsi de suite:

class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate

Dans votre viewDidLoadajoutez les lignes de code suivantes:

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

Maintenant, tout ce que vous avez à faire pour afficher l'état de vide est:

//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)
}

Ces méthodes ne sont pas obligatoires, il est également possible d'afficher simplement l'état vide sans bouton, etc.

Pour 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)
}
Hapeki
la source
J'ai des problèmes avec DZEmptyDataSet qui n'est pas correctement aligné verticalement. Quelqu'un at-il eu ce même problème?
amariduran le
12

Une façon de le faire serait de modifier votre source de données pour qu'elle renvoie 1lorsque le nombre de lignes est égal à zéro et de produire une cellule spéciale (peut-être avec un identificateur de cellule différent) dans la tableView:cellForRowAtIndexPath:méthode.

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

Cela peut devenir quelque peu compliqué si vous avez plusieurs contrôleurs de vue de table que vous devez maintenir, car quelqu'un finira par oublier d'insérer un contrôle zéro. Une meilleure approche consiste à créer une implémentation distincte d'une UITableViewDataSourceimplémentation qui renvoie toujours une seule ligne avec un message configurable (appelons-la EmptyTableViewDataSource). Lorsque les données gérées par votre contrôleur de vue table changent, le code qui gère la modification vérifie si les données sont vides. S'il n'est pas vide, définissez votre contrôleur de vue de table avec sa source de données régulière; sinon, définissez-le avec une instance de EmptyTableViewDataSourcequi a été configurée avec le message approprié.

dasblinkenlight
la source
5
J'ai eu des problèmes avec les tables dont les lignes ont été supprimées, deleteRowsAtIndexPaths:withRowAnimation:car le nombre renvoyé par numberOfRowsInSectiondoit correspondre au résultat de $ numRows - $ rowsDeleted.
Animal451
4
Je recommande vivement contre cela. Il énonce votre code avec des cas extrêmes.
xtravar
2
@xtravar Plutôt que de voter contre, pensez à publier votre propre réponse.
dasblinkenlight
1
J'ai suivi cette voie et cela conduit à son propre éventail de problèmes. D'après mon expérience, il est presque toujours préférable d'augmenter les API Apple plutôt que d'essayer de les remplacer.
xtravar
1
Cette solution ne fonctionnera pas avec NSFetchedResultsController. Je vais plutôt essayer l'approche backgroundView.
Sam
10

J'utilise le message titleForFooterInSection pour cela. Je ne sais pas si c'est sous-optimal ou non, mais ça marche.

-(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;
}
Carlos B
la source
2
Belle astuce et me semble assez propre. Voici un one-liner:return tableView.numberOfRowsInSection(section) == 0 ? "This list is now empty" : nil
TruMan1
8

Donc pour une solution plus sûre:

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

et pour UICollectionViewaussi:

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

Solution plus générique:

    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
     }
}
Meseery
la source
7

Je ne peux que recommander de faire glisser et déposer un UITextView dans le TableView après les cellules. Connectez-vous au ViewController et masquez-le / affichez-le le cas échéant (par exemple chaque fois que la table se recharge).

entrez la description de l'image ici

pasql
la source
C'est le moyen le plus simple)!
moonvader
Un mal de tête si douloureux à la pomme; merci pour cette idée
Eenvincible
5

Utiliser le backgroundView est très bien, mais il ne défile pas bien comme dans Mail.app.

J'ai fait quelque chose de similaire à ce qu'a fait xtravar .

J'ai ajouté une vue en dehors de la hiérarchie des vues du tableViewController. hiérarchie

Ensuite, j'ai utilisé le code suivant dans 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

Fondamentalement, j'ai ajouté le en emptyStateViewtant que sous-vue de l' tableViewobjet. Comme les séparateurs chevaucheraient la vue, j'ai défini leur couleur sur clearColor. Pour revenir à la couleur de séparation par défaut, vous pouvez simplement la définir sur nil.

mangerlahn
la source
Cela fonctionne plutôt bien! Mais si vous avez des éléments à rafraîchir par exemple, cela ne défile pas avec eux. J'ai trouvé préférable de définir le tableHeaderView, et de m'assurer qu'il est redimensionné pour s'adapter .
Hannele
4

Selon Apple, utiliser un contrôleur de vue de conteneur est la bonne façon de le faire .

J'ai mis toutes mes vues d'état vides dans un Storyboard séparé. Chacun sous sa propre sous-classe UIViewController. J'ajoute du contenu directement sous leur vue racine. Si une action / un bouton est nécessaire, vous avez maintenant déjà un contrôleur pour le gérer.
Ensuite, il suffit d'instancier le contrôleur de vue souhaité à partir de ce Storyboard, de l'ajouter en tant que contrôleur de vue enfant et d'ajouter la vue de conteneur à la hiérarchie de tableView (sous-vue). Votre vue d'état vide pourra également faire défiler, ce qui vous convient et vous permet d'implémenter pull to refresh.

Lisez le chapitre «Ajout d'un contrôleur de vue enfant à votre contenu» pour obtenir de l'aide sur la façon de l'implémenter.

Assurez-vous simplement de définir le cadre de vue enfant comme (0, 0, tableView.frame.width, tableView.frame.height)et les choses seront centrées et alignées correctement.

AmitP
la source
3

Premièrement, les problèmes avec d'autres approches populaires.

Vue en arrière-plan

La vue d'arrière-plan ne se centre pas bien si vous deviez utiliser le cas simple de le définir comme un UILabel.

Cellules, en-têtes ou pieds de page pour afficher le message

Cela interfère avec votre code fonctionnel et introduit des cas extrêmes. Si vous voulez parfaitement centrer votre message, cela ajoute un autre niveau de complexité.

Faire rouler votre propre contrôleur de vue de table

Vous perdez les fonctionnalités intégrées, telles que refreshControl, et réinventez la roue. Restez fidèle à UITableViewController pour les meilleurs résultats maintenables.

Ajout de UITableViewController en tant que contrôleur de vue enfant

J'ai le sentiment que vous vous retrouverez avec des problèmes de contentInset dans iOS 7+ - et pourquoi compliquer les choses?

Ma solution

La meilleure solution que j'ai trouvée (et, certes, ce n'est pas l'idéal) est de créer une vue spéciale qui puisse être placée au-dessus d'une vue de défilement et agir en conséquence. Cela se complique évidemment dans iOS 7 avec la folie contentInset, mais c'est faisable.

Choses à surveiller:

  • les séparateurs de table sont amenés au premier plan à un moment donné pendant reloadData - vous devez vous en protéger
  • contentInset / contentOffset - observez ces touches sur votre vue spéciale
  • clavier - si vous ne voulez pas que le clavier vous gêne, c'est un autre calcul
  • mise en page automatique - vous ne pouvez pas dépendre des changements de cadre pour positionner votre vue

Une fois que vous avez compris cela une fois dans une sous-classe UIView, vous pouvez l'utiliser pour tout - charger des spinners, désactiver des vues, afficher des messages d'erreur, etc.

xtravar
la source
Pouvez-vous élaborer dans votre réponse. Comment savoir si la table est vide? Pour ensuite «agir en conséquence».
Michael Ozeryansky
Vous savez que votre table est vide parce que vous êtes celui qui remplit votre table. Ainsi, dans votre contrôleur de vue, vous auriez un rappel de chargement asynchrone. Dans ce rappel, if (itemsToShow.count == 0) {ajoute votre vue à la vue de défilement}. La partie la plus délicate consiste à positionner cette vue de dessus ('Shield / Message View') pour occuper tout le cadre de la vue scroll, moins contentInset, etc., de sorte que le message soit centré verticalement.
xtravar
Comment ajouter une vue en haut de la vue tableau? self.view est la vue tableau et si j'utilise, addSubViewelle est attachée à la vue tableau, ce qui pose toujours des problèmes avec la mise en page automatique.
test
La vue que vous placez dans la vue de table ne doit pas utiliser la disposition automatique et la vue de table elle-même n'utilise pas la disposition automatique.
xtravar
1
Vous avez manqué une approche là-bas; Vous pouvez utiliser un UITableViewController et l'ajouter en tant que contrôleur de vue enfant à un UIViewController régulier
user1169629
3

C'est la solution la meilleure et la plus simple.

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;
Rajesh Maurya
la source
2

Afficher le message pour une liste vide, que ce soit son UITableView ou 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
        }
    }
}

Coutumes:

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

OU:

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

N'oubliez pas: n'oubliez pas de supprimer l'étiquette du message au cas où les données viendraient après l'actualisation.

Gurjit Singh
la source
1
J'ajouterais une condition if if messages.count! = 0 {show}
Eddwin Paz
1

Sélectionnez votre tableviewController Scene dans le storyboard

entrez la description de l'image ici

Faites glisser et déposez UIView Ajouter une étiquette avec votre message (par exemple: Aucune donnée)

entrez la description de l'image ici

créer une sortie d'UIView (par exemple, yournoDataView) sur votre TableViewController.

et dans viewDidLoad

self.tableView.backgroundView = votreNoDataView

Gaurav Bhardwaj
la source
1

Utilisation de 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
}
M Murteza
la source
1

Vous pouvez l'ajouter à votre classe de 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)
}

Montrez-le comme ceci dans la classe sur l'obtention des données de l'API.

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

Cachez-le comme ça.

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

func hideNoDataMessage(){
    messageLabel.removeFromSuperview()
}
Mudassir Asghar
la source
Hey @ Mudassir-Asghar à quoi ressemble la fonction hideNoDataMessage?
Saamer le
1
Salut, @Saamer je viens de mettre à jour la réponse ci-dessus. merci de l'avoir signalé, j'espère que cela vous aidera :)
Mudassir Asghar
0

Version Swift mais forme meilleure et plus simple. ** 3,0

J'espère que cela 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
0

Probablement pas la meilleure solution, mais je l'ai fait en mettant simplement une étiquette au bas de mon tableau et si les lignes = 0, je lui attribue du texte. Assez facile et réalise ce que vous essayez de faire avec quelques lignes de code.

J'ai deux sections dans mon tableau (emplois et écoles)

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

    if (jobs.count == 0 && schools.count == 0) {
        emptyLbl.text = "No jobs or schools"
    } else {
        emptyLbl.text = ""
    }
Zach
la source
Vous pouvez également remplacer l'étiquette par une vue d'image vide lorsque count = 0 si vous le souhaitez.
Zach
0

Le moyen le plus simple et le plus rapide de le faire est de faire glisser une étiquette sur le panneau latéral sous tableView. Créez une sortie pour l'étiquette et la tableView et ajoutez une instruction if pour masquer et afficher l'étiquette et la table si nécessaire. Vous pouvez également ajouter tableView.tableFooterView = UIView (frame: CGRect.zero) à viewDidLoad () pour donner à une table vide l'impression qu'elle est masquée si la table et la vue d'arrière-plan ont la même couleur.

yoamod
la source
(Ce message ne semble pas fournir une réponse de qualité à la question. Veuillez modifier votre réponse et la compléter avec des informations plus détaillées, ou simplement la publier en tant que commentaire à la question).
sɐunıɔ ןɐ qɐp
0

J'ai apporté quelques modifications afin que nous n'ayons pas besoin de vérifier le décompte manuellement, j'ai également ajouté des contraintes pour l'étiquette afin que rien ne se passe mal quelle que soit la taille du message, comme indiqué ci-dessous:

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

Usage

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

Ou vous pouvez également utiliser une bibliothèque légère personnalisable

SwiftEmptyState

Enes Karaosman
la source