Manipulando um UITableView vazio. Imprimir uma mensagem amigável

124

Eu tenho um UITableView que, em alguns casos, é legal estar vazio. Portanto, em vez de mostrar a imagem de plano de fundo do aplicativo, prefiro imprimir uma mensagem amigável na tela, como:

Esta lista está agora vazia

Qual é a maneira mais simples de fazer isso?

estado de
fonte
10
Confira este projeto: github.com/dzenbot/DZNEmptyDataSet
oleynikd
@oleynikd Thank you !!!!!
Luda

Respostas:

173

A propriedade backgroundView do UITableView é sua amiga.

Em viewDidLoadou em qualquer lugar que você reloadDatadeve determinar se a sua tabela está vazia ou não e atualize a propriedade backgroundView do UITableView com uma UIView contendo um UILabel ou apenas defina-a como nula. É isso aí.

É claro que é possível fazer com que a fonte de dados do UITableView cumpra duas funções e retorne uma célula especial "list is empty", isso me parece uma ilusão. De repente, numberOfRowsInSection:(NSInteger)sectioné preciso calcular o número de linhas de outras seções que não foram solicitadas para garantir que elas também estejam vazias. Você também precisa criar uma célula especial que tenha a mensagem vazia. Além disso, não esqueça que você provavelmente precisará alterar a altura do seu celular para acomodar a mensagem vazia. Tudo isso é possível, mas parece um band-aid em cima do band-aid.

Ray Fix
fonte
12
O backgroundView é a melhor solução, eu acho. Obrigado!
Ferran Maylinch
1
Uma desvantagem é que, se a exibição da tabela for puxada para baixo, a mensagem permanecerá em sua posição. Eu gostaria de ter como no aplicativo da loja de aplicativos em atualizações. Aqui a mensagem vazia acompanha o comportamento da rolagem ...
testando
@ testing, obviamente, se você precisar de mensagens vazias para rolar, não poderá usar a exibição em segundo plano, pois isso não faz parte da hierarquia de rolagem. Você provavelmente deseja usar uma seção personalizada e UITableViewCell para o estado vazio.
LightningStryk
Alternar backgroundViewpropriedade oculta é o caminho a percorrer. Com DZNEmptyDataSet, temos que usar o seuemptyDataSetSource
onmyway133 /
Se você deseja adicionar botões, deve fazer: tableView.backgroundView!.userInteraction = trueapós a linha que você definiu tableView.backgroundView = constructMyViewWithButtons(), ou como você o definiu.
Kbpontius #
90

O mesmo que a resposta de Jhonston, mas eu a preferi como uma extensão:

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

Uso:

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
fonte
Obrigado pela sua sugestão.
precisa saber é o seguinte
1
Extensão é melhor! Obrigado.
Ogulcan Orhan
Amo a extensão :)
Alix
1
Se você tiver seções, precisará mover essa lógica para o func numberOfSections(in tableView: UITableView) -> Intmétodo
crie
87

Com base nas respostas aqui, aqui está uma aula rápida que eu fiz que você pode usar no seu 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;
    }
}

No seu UITableViewControllervocê pode chamar isso emnumberOfSectionsInTableView(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
    }
}

insira a descrição da imagem aqui

Com uma pequena ajuda de http://www.appcoda.com/pull-to-refresh-uitableview-empty/

Johnston
fonte
também gostaria de adicionar uma imagem a ele. Mas uma ótima solução rápida.
AnBisw
10
usar uma extensão em vez de uma classe (não passar no controlador de vista)
Cesare
Não consegui que o código acima funcionasse para mim, mas consegui que o código nesta resposta (primeira) funcionasse, caso outras pessoas tivessem o mesmo problema. Também acho que o 2º resposta iria funcionar tão bem (usando o cais cena) - stackoverflow.com/questions/28532926/...
Renee Olson
viewController.tableView.separatorStyle = .nonenão é obrigatório.
Dmitry
17

Eu recomendo a seguinte biblioteca: DZNEmptyDataSet

A maneira mais fácil de adicioná-lo ao seu projeto é usá-lo com Cocaopods assim: pod 'DZNEmptyDataSet'

No seu TableViewController, adicione a seguinte declaração de importação (Swift):

import DZNEmptyDataSet

Em seguida, certifique-se de seus conforma classe para o DNZEmptyDataSetSourcee DZNEmptyDataSetDelegateassim:

class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate

No seu, viewDidLoadadicione as seguintes linhas de código:

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

Agora tudo o que você precisa fazer para mostrar o estado vazio é:

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

Esses métodos não são obrigatórios, também é possível apenas mostrar o estado vazio sem um botão etc.

Para 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
fonte
Estou tendo problemas com o DZEmptyDataSet não sendo alinhado verticalmente corretamente. Alguém já teve esse mesmo problema?
amariduran 27/09/18
12

Uma maneira de fazer isso seria modificar sua fonte de dados para retornar 1quando o número de linhas for zero e produzir uma célula de finalidade especial (talvez com um identificador de célula diferente) no tableView:cellForRowAtIndexPath:método.

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

Isso pode ficar um pouco complicado se você tiver vários controladores de exibição de tabela que precisam manter, porque alguém se esquecerá de inserir uma verificação zero. Uma abordagem melhor é criar uma implementação separada de uma UITableViewDataSourceimplementação que sempre retorne uma única linha com uma mensagem configurável (vamos chamá-lo EmptyTableViewDataSource). Quando os dados gerenciados pelo seu controlador de exibição de tabela são alterados, o código que gerencia a alteração verifica se os dados estão vazios. Se não estiver vazio, configure seu controlador de exibição de tabela com sua fonte de dados regular; caso contrário, configure-o com uma instância do EmptyTableViewDataSourceque foi configurada com a mensagem apropriada.

dasblinkenlight
fonte
5
Eu tive problemas ao fazer isso com tabelas com linhas excluídas, deleteRowsAtIndexPaths:withRowAnimation:pois o número retornado das numberOfRowsInSectionnecessidades corresponde ao resultado de $ numRows - $ linesDeleted.
precisa saber é o seguinte
4
Eu recomendo muito, altamente contra isso. Cria seu código com casos extremos.
Xtravar
2
@xtravar Em vez de reduzir a votação, considere postar sua própria resposta.
dasblinkenlight
1
Eu segui esse caminho e leva a sua própria variedade de problemas. Na minha experiência, quase sempre é melhor aumentar as APIs da Apple do que tentar substituí-las.
Xtravar
1
Esta solução não funcionará com NSFetchedResultsController. Vou tentar a abordagem backgroundView.
6134 Sam
10

Eu tenho usado a mensagem titleForFooterInSection para isso. Não sei se isso é subótimo ou não, mas funciona.

-(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
fonte
2
Bom truque e parece bastante limpo para mim. Aqui está uma lista:return tableView.numberOfRowsInSection(section) == 0 ? "This list is now empty" : nil
TruMan1 /
8

Portanto, para uma solução mais segura:

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

e UICollectionViewtambém:

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

Solução mais genérica:

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

Só posso recomendar arrastar e soltar um UITextView dentro do TableView após as células. Faça uma conexão com o ViewController e oculte / exiba quando apropriado (por exemplo, sempre que a tabela for recarregada).

insira a descrição da imagem aqui

pasql
fonte
Essa é a maneira mais fácil)!
Moonvader 5/05
Uma dor de cabeça tão dolorosa à maneira da maçã; obrigado por essa idéia
Eenvincible
5

Usar o backgroundView é bom, mas não rola muito bem como no Mail.app.

Eu fiz algo semelhante ao que o xtravar fez.

Eu adicionei uma visualização fora da hierarquia da tableViewController. hierarquia

Então eu usei o seguinte código em 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

Basicamente, eu adicionei o emptyStateViewcomo uma subview do tableViewobjeto. Como os separadores se sobrepõem à vista, defino sua cor como clearColor. Para voltar à cor padrão do separador, basta configurá-lo como nil.

mangerlahn
fonte
Isso funciona muito bem! Mas se você tem elementos para puxar para atualizar, por exemplo, isso não rola com eles. Achei melhor definir tableHeaderViewe redimensionar para caber .
Hannele
4

Usar um Container View Controller é a maneira correta de fazê-lo, de acordo com a Apple .

Coloquei todas as minhas visualizações de estado vazias em um Storyboard separado. Cada um abaixo da sua própria subclasse UIViewController. Eu adiciono conteúdo diretamente na visualização raiz. Se qualquer ação / botão for necessária, agora você já possui um controlador para lidar com isso.
Depois, basta instanciar o controlador de exibição desejado a partir desse Storyboard, adicione-o como um controlador de exibição filho e adicione a exibição de contêiner à hierarquia do tableView (sub-exibição). Sua exibição de estado vazio também pode ser rolada, o que é bom e permite que você implemente pull to refresh.

Leia o capítulo 'Adicionando um controlador de exibição infantil ao seu conteúdo' para obter ajuda sobre como implementar.

Apenas certifique-se de definir o quadro de exibição filho como (0, 0, tableView.frame.width, tableView.frame.height)e as coisas serão centralizadas e alinhadas corretamente.

AmitP
fonte
3

Primeiro, os problemas com outras abordagens populares.

BackgroundView

A exibição de plano de fundo não é muito boa se você usar o simples caso de defini-la como um UILabel.

Células, cabeçalhos ou rodapés para exibir a mensagem

Isso interfere no seu código funcional e apresenta casos extremos estranhos. Se você deseja centralizar perfeitamente sua mensagem, isso adiciona outro nível de complexidade.

Rolando seu próprio controlador de exibição de tabela

Você perde a funcionalidade interna, como refreshControl, e reinventa a roda. Atenha-se ao UITableViewController para obter os melhores resultados de manutenção.

Adicionando UITableViewController como um controlador de exibição filho

Tenho a sensação de que você terminará com os problemas contentInset no iOS 7+ - além de por que complicar as coisas?

Minha solução

A melhor solução que eu encontrei (e, concedido, isso não é o ideal) é criar uma visualização especial que possa ficar no topo de uma visualização de rolagem e agir de acordo. Obviamente, isso fica complicado no iOS 7 com a loucura contentInset, mas é factível.

Itens a serem observados:

  • separadores de tabela são trazidos para a frente em algum momento durante o reloadData - você precisa se proteger contra isso
  • contentInset / contentOffset - observe essas chaves em sua exibição especial
  • teclado - se você não quer que o teclado atrapalhe, esse é outro cálculo
  • autolayout - você não pode depender de alterações de quadro para posicionar sua visualização

Depois de descobrir isso uma vez em uma subclasse do UIView, você pode usá-lo para tudo - carregar spinners, desativar visualizações, mostrar mensagens de erro etc.

xtravar
fonte
Você pode elaborar sua resposta. Como você sabe se a mesa está vazia? Para então "agir em conformidade".
Michael Ozeryansky
Você sabe que sua mesa está vazia porque é você quem está preenchendo sua mesa. Portanto, no seu controlador de exibição, você teria um retorno de chamada de carregamento assíncrono. Nesse retorno de chamada, se (itemsToShow.count == 0) {adicione sua visualização à visualização de rolagem}. A parte complicada é fazer com que a exibição superior ('exibição de escudo / mensagem') seja posicionada para ocupar todo o quadro da exibição de rolagem, menos contentInset etc., para que a mensagem seja centralizada verticalmente.
Xtravar
Como você adiciona uma exibição na parte superior da tabela? self.view é a visualização da tabela e, se eu a usar addSubView, é anexada à visualização da tabela, o que sempre causa problemas no layout automático.
testando
A exibição que você coloca na exibição da tabela não deve usar o autolayout e a tableview em si não usa o autolayout.
Xtravar
1
Você perdeu uma abordagem lá; Você poderia usar um UITableViewController e adicioná-lo como um controlador de vista criança a um UIViewController regulares
user1169629
3

Esta é a melhor e mais simples solução.

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
fonte
2

Mostrar mensagem para lista vazia, seja seu 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
        }
    }
}

Usos:

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

OU:

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

Lembre-se: Não esqueça de remover o rótulo da mensagem, caso os dados cheguem após a atualização.

Gurjit Singh
fonte
1
Eu gostaria de acrescentar uma condição if se messages.count = 0 {Mostrar}!
Eddwin Paz
1

Cena do controlador no storyboard

insira a descrição da imagem aqui

Arraste e solte UIView Adicione um marcador com sua mensagem (por exemplo: Sem dados)

insira a descrição da imagem aqui

crie uma saída do UIView (por exemplo, yournoDataView) no seu TableViewController.

e em viewDidLoad

self.tableView.backgroundView = yourNoDataView

gaurav bhardwaj
fonte
1

Usando o 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
fonte
1

Você pode adicionar isso à sua classe 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)
}

Mostre dessa forma na classe sobre como obter os dados da API.

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

Esconda assim.

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

func hideNoDataMessage(){
    messageLabel.removeFromSuperview()
}
Mudassir Asghar
fonte
Hey @ Mudassir-Asghar, como é a função hideNoDataMessage?
Saamer 16/04
1
Olá, @Saamer, acabei de atualizar a resposta acima. obrigado por apontar, espero que isso ajude :)
Mudassir Asghar
0

Versão rápida, mas melhor e mais simples. ** 3,0

Espero que servidor seu propósito ......

No seu 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 auxiliar com função:

 /* 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
fonte
0

Provavelmente não é a melhor solução, mas fiz isso colocando um rótulo na parte inferior da minha tabela e, se as linhas = 0, atribuo-lhe algum texto. Muito fácil e consegue o que você está tentando fazer com algumas linhas de código.

Tenho duas seções na minha mesa (empregos e escolas)

    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
fonte
Você também pode substituir o rótulo com um ImageView vazio quando count = 0 se você queria ..
Zach
0

A maneira mais fácil e rápida de fazer isso é arrastar um rótulo para o painel lateral sob tableView. Crie uma saída para o rótulo e o tableView e adicione uma instrução if para ocultar e mostrar o rótulo e a tabela conforme necessário. Como alternativa, você pode adicionar tableView.tableFooterView = UIView (frame: CGRect.zero) a isto viewDidLoad () para dar a uma tabela vazia a percepção de que ela está oculta se a tabela e a exibição em segundo plano tiverem a mesma cor.

yoamod
fonte
(Esta postagem parece não fornecer uma resposta de qualidade à pergunta. Edite sua resposta e complete-a com informações mais detalhadas ou apenas publique-a como um comentário à pergunta).
sɐunıɔ ןɐ qɐp
0

Fiz algumas alterações para não precisarmos verificar a contagem manualmente, também adicionei restrições para o rótulo para que nada dê errado, não importa o tamanho da mensagem, como mostrado abaixo:

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

Uso

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

Ou, como alternativa, você pode usar uma biblioteca leve e personalizável

SwiftEmptyState

Enes Karaosman
fonte