Manejo de un UITableView vacío. Imprime un mensaje amigable

124

Tengo un UITableView que en algunos casos es legal estar vacío. Entonces, en lugar de mostrar la imagen de fondo de la aplicación, preferiría imprimir un mensaje amigable en la pantalla, como:

Esta lista ahora está vacía

¿Cuál es la forma más sencilla de hacerlo?

cateof
fuente
10
Echa un vistazo a este proyecto: github.com/dzenbot/DZNEmptyDataSet
oleynikd
@oleynikd ¡Gracias!
Luda

Respuestas:

173

La propiedad backgroundView de UITableView es tu amiga.

En viewDidLoado en cualquier lugar que reloadDatadeba determinar si su tabla está vacía o no, y actualice la propiedad backgroundView de UITableView con un UIView que contenga un UILabel o simplemente configúrelo como nulo. Eso es.

Por supuesto, es posible hacer que la fuente de datos de UITableView realice una doble tarea y devuelva una celda especial "la lista está vacía", me parece un error. De repente, numberOfRowsInSection:(NSInteger)sectiontiene que calcular el número de filas de otras secciones sobre las que no se le preguntó para asegurarse de que también estén vacías. También necesita hacer una celda especial que tenga el mensaje vacío. Además, no olvide que probablemente necesite cambiar la altura de su celda para acomodar el mensaje vacío. Todo esto es factible, pero parece una curita además de la curita.

Ray Fix
fuente
12
El backgroundView es la mejor solución, creo. ¡Gracias!
Ferran Maylinch
1
Una desventaja es que si se despliega la vista de tabla, el mensaje permanece en su posición. Prefiero tener Me gusta en la aplicación de la tienda de aplicaciones en actualizaciones. Aquí el mensaje vacío va con el comportamiento de desplazamiento ...
prueba el
@testing obviamente si necesita la mensajería vacía para desplazarse, no puede usar la vista de fondo, ya que eso no es parte de la jerarquía de desplazamiento. Probablemente quiera usar una sección personalizada y UITableViewCell para el estado vacío.
LightningStryk
Alternar backgroundViewpropiedad oculta es el camino a seguir. Con DZNEmptyDataSet, tenemos que usar suemptyDataSetSource
onmyway133
Si desea agregar botones, debe hacer lo siguiente: tableView.backgroundView!.userInteraction = truedespués de la línea que configuró tableView.backgroundView = constructMyViewWithButtons(), o como lo configuró.
kbpontius
90

Igual que la respuesta de Jhonston, pero la preferí como extensión:

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
fuente
Gracias por su sugerencia.
ssowri1
1
¡La extensión es mejor! Gracias.
Ogulcan Orhan
Me encanta la extensión :)
Alix
1
Si tiene secciones, entonces deberá mover esta lógica al func numberOfSections(in tableView: UITableView) -> Intmétodo
cree el
87

Basado en las respuestas aquí, aquí hay una clase rápida que hice que puedes usar en tu 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;
    }
}

En tu UITableViewControllerpuedes llamar a esto ennumberOfSectionsInTableView(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
    }
}

ingrese la descripción de la imagen aquí

Con un poco de ayuda de http://www.appcoda.com/pull-to-refresh-uitableview-empty/

Johnston
fuente
Sin embargo, también me gustaría agregarle una imagen. Pero una gran solución rápida.
AnBisw
10
use una extensión en lugar de una clase (no pase el controlador de vista)
Cesare
No pude hacer que el código anterior funcionara para mí, pero pude hacer que el código en esta respuesta (la primera) funcionara, en caso de que otros tengan el mismo problema. También creo que la segunda respuesta podría funcionar tan bien (utilizando la base de la escena) - stackoverflow.com/questions/28532926/...
Renee Olson
viewController.tableView.separatorStyle = .noneNo es obligatorio
Dmitry
17

Recomiendo la siguiente biblioteca: DZNEmptyDataSet

La forma más fácil de agregarlo en su proyecto es usarlo con Cocaopods de esta manera: pod 'DZNEmptyDataSet'

En su TableViewController agregue la siguiente declaración de importación (Swift):

import DZNEmptyDataSet

Luego, asegúrese de que su clase se ajuste al DNZEmptyDataSetSourcey DZNEmptyDataSetDelegateasí:

class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate

En su viewDidLoadagregar las siguientes líneas de código:

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

Ahora todo lo que tiene que hacer para mostrar el estado vacío es:

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

Estos métodos no son obligatorios, también es posible mostrar el estado vacío sin un botón, 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
fuente
Tengo problemas con DZEmptyDataSet que no se alinean verticalmente correctamente. ¿Alguien ha tenido este mismo problema?
amariduran
12

Una forma de hacerlo sería modificar su fuente de datos para que regrese 1cuando el número de filas sea cero y para producir una celda de propósito especial (quizás con un identificador de celda diferente) en el 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
    ...
}

Esto puede ser algo complicado si tiene múltiples controladores de vista de tabla que necesita mantener, porque alguien eventualmente olvidará insertar una verificación cero. Un mejor enfoque es crear una implementación separada de una UITableViewDataSourceimplementación que siempre devuelva una sola fila con un mensaje configurable (llamémoslo EmptyTableViewDataSource). Cuando los datos administrados por su controlador de vista de tabla cambian, el código que administra el cambio verificará si los datos están vacíos. Si no está vacío, configure su controlador de vista de tabla con su fuente de datos regular; de lo contrario, EmptyTableViewDataSourceconfigúrelo con una instancia del que se haya configurado con el mensaje apropiado.

dasblinkenlight
fuente
55
He tenido problemas para hacer esto con tablas que tienen filas eliminadas deleteRowsAtIndexPaths:withRowAnimation:ya que el número devuelto numberOfRowsInSectiondebe coincidir con el resultado de $ numRows - $ rowsDeleted.
Animal451
44
Lo recomiendo encarecidamente contra esto. Acribilla su código con casos extremos.
xtravar
2
@xtravar En lugar de votar negativamente, considere publicar su propia respuesta.
dasblinkenlight
1
He seguido ese camino, y eso lleva a su propia gama de problemas. En mi experiencia, casi siempre es mejor aumentar las API de Apple que intentar reemplazarlas.
xtravar
1
Esta solución no funcionará con NSFetchedResultsController. Voy a probar el enfoque backgroundView en su lugar.
Sam
10

He estado usando el mensaje titleForFooterInSection para esto. No sé si esto es subóptimo o no, pero 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
fuente
2
Buen truco y me parece bastante limpio. Aquí hay una return tableView.numberOfRowsInSection(section) == 0 ? "This list is now empty" : nil
frase
8

Entonces, para una solución más 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
  }
}

y para UICollectionViewtambién:

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

Más solución 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
fuente
7

Solo puedo recomendar arrastrar y soltar un UITextView dentro de TableView después de las celdas. Haga una conexión con el ViewController y escóndelo / muéstrelo cuando sea apropiado (por ejemplo, cada vez que se recargue la tabla).

ingrese la descripción de la imagen aquí

pasql
fuente
Esa es la forma más fácil)!
moonvader
Un dolor de cabeza tan doloroso al estilo de las manzanas; gracias por esta idea
Eenvincible
5

Usar backgroundView está bien, pero no se desplaza bien como en Mail.app.

Hice algo similar a lo que hizo xtravar .

Agregué una vista fuera de la jerarquía de vistas de tableViewController. jerarquía

Luego usé el siguiente código en 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

Básicamente agregué el emptyStateViewcomo una subvista del tableViewobjeto. Como los separadores se superpondrían a la vista, configuré su color en clearColor. Para volver al color del separador predeterminado, solo puede configurarlo nil.

Mangerlahn
fuente
¡Esto funciona bastante bien! Pero si tiene elementos para actualizar, por ejemplo, esto no se desplaza con ellos. Encontré mejor configurarlo tableHeaderViewy asegurarme de que cambie su tamaño para ajustarse .
Hannele
4

Usar un controlador de vista de contenedor es la forma correcta de hacerlo según Apple .

Puse todas mis vistas de estado vacías en un Storyboard separado. Cada uno bajo su propia subclase UIViewController. Agrego contenido directamente debajo de su vista raíz. Si se necesita alguna acción / botón, ahora ya tiene un controlador para manejarlo.
Entonces es solo una cuestión de instanciar el controlador de vista deseado de ese Storyboard, agregarlo como un controlador de vista secundario y agregar la vista de contenedor a la jerarquía de tableView (vista secundaria). Su vista de estado vacía también será desplazable, lo que se siente bien y le permite implementar pull para actualizar.

Lea el capítulo 'Agregar un controlador de vista infantil a su contenido' para obtener ayuda sobre cómo implementarlo.

Solo asegúrese de establecer el marco de vista secundario como (0, 0, tableView.frame.width, tableView.frame.height)y las cosas estarán centradas y alineadas correctamente.

AmitP
fuente
3

Primero, los problemas con otros enfoques populares.

Vista de fondo

La vista de fondo no se centra bien si fuera a utilizar el simple caso de configurarlo para que sea un UILabel.

Celdas, encabezados o pies de página para mostrar el mensaje

Esto interfiere con su código funcional e introduce casos extraños. Si desea centrar perfectamente su mensaje, eso agrega otro nivel de complejidad.

Rodando su propio controlador de vista de mesa

Pierde la funcionalidad incorporada, como refreshControl, y reinventa la rueda. Apéguese a UITableViewController para obtener los mejores resultados mantenibles.

Agregar UITableViewController como controlador de vista secundario

Tengo la sensación de que terminarás con problemas de ContentInset en iOS 7+, además, ¿por qué complicar las cosas?

Mi solución

La mejor solución que se me ocurrió (y, por supuesto, esto no es lo ideal) es hacer una vista especial que se pueda colocar encima de una vista de desplazamiento y actuar en consecuencia. Obviamente, esto se complica en iOS 7 con la locura ContentInset, pero es factible.

Cosas que debes tener en cuenta:

  • los separadores de mesa se ponen al frente en algún momento durante reloadData; debe protegerse contra eso
  • contentInset / contentOffset: observe esas teclas en su vista especial
  • teclado: si no desea que el teclado se interponga, ese es otro cálculo
  • Autolayout: no puede depender de los cambios de marco para posicionar su vista

Una vez que haya resuelto esto una vez en una subclase de UIView, puede usarlo para todo: cargar hiladores, deshabilitar vistas, mostrar mensajes de error, etc.

xtravar
fuente
¿Puedes dar más detalles en tu respuesta? ¿Cómo saber si la mesa está vacía? Para luego "actuar en consecuencia".
Michael Ozeryansky
Usted sabe que su mesa está vacía porque usted es quien llena su mesa. Entonces, en su controlador de vista, tendría una devolución de llamada de carga asincrónica. En esa devolución de llamada, if (itemsToShow.count == 0) {agregue su vista a la vista de desplazamiento}. La parte difícil es hacer que esa vista superior ('vista de escudo / mensaje') se coloque para ocupar todo el marco de la vista de desplazamiento, menos el contenido de inserción, etc., de modo que el mensaje esté centrado verticalmente.
xtravar
¿Cómo se agrega una vista en la parte superior de la vista de tabla? self.view es la vista de tabla y, si la uso, addSubViewse adjunta a la vista de tabla, lo que siempre genera problemas con el diseño automático.
prueba el
La vista que coloque dentro de la vista de tabla no debe usar la distribución automática, y la vista de tabla en sí no usa la distribución automática.
xtravar
1
Te perdiste un enfoque allí; Puede usar un UITableViewController y agregarlo como un controlador de vista secundario a un UIViewController normal
usuario1169629
3

Esta es la mejor y más simple solución.

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

Mostrar mensaje para lista vacía, moje su UITableView o 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!")
}

O:

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

Recuerde: no olvide eliminar la etiqueta del mensaje en caso de que los datos lleguen después de la actualización.

Gurjit Singh
fuente
1
Agregaría
1

Seleccione su tableviewController Scene en storyboard

ingrese la descripción de la imagen aquí

Arrastre y suelte la etiqueta Agregar UIView con su mensaje (por ejemplo: Sin datos)

ingrese la descripción de la imagen aquí

cree una salida de UIView (digamos, por ejemplo, yournoDataView) en su TableViewController.

y en viewDidLoad

self.tableView.backgroundView = yourNoDataView

gaurav bhardwaj
fuente
1

Usando 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
fuente
1

Puede agregar esto a su clase 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)
}

Muéstrelo así en la clase sobre cómo obtener los datos de la API.

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

Escóndelo así.

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

func hideNoDataMessage(){
    messageLabel.removeFromSuperview()
}
Mudassir Asghar
fuente
Hola @ Mudassir-Asghar, ¿cómo es la función hideNoDataMessage?
Saamer
1
Hola, @Saamer, acabo de actualizar la respuesta anterior. gracias por señalarlo, espero que esto ayude :)
Mudassir Asghar
0

Versión rápida pero mejor y más simple. ** 3.0

Espero que sirva su propósito ......

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

Clase auxiliar con función:

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

Probablemente no sea la mejor solución, pero lo hice simplemente poniendo una etiqueta en la parte inferior de mi tabla y si las filas = 0, entonces le asigno algo de texto. Bastante fácil y logra lo que intenta hacer con unas pocas líneas de código.

Tengo dos secciones en mi mesa (trabajos y escuelas)

    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
fuente
También podría reemplazar la etiqueta con una vista de imagen vacía cuando count = 0 si lo desea ..
Zach
0

La forma más fácil y rápida de hacer esto es arrastrar una etiqueta al panel lateral debajo de tableView. Cree una salida para la etiqueta y tableView y agregue una instrucción if para ocultar y mostrar la etiqueta y la tabla según sea necesario. Alternativamente, puede agregar tableView.tableFooterView = UIView (frame: CGRect.zero) esto a viewDidLoad () para dar a una tabla vacía la percepción de que está oculta si la vista de tabla y de fondo tienen el mismo color.

yoamod
fuente
(Esta publicación no parece proporcionar una respuesta de calidad a la pregunta. Edite su respuesta y complétela con información más detallada, o simplemente publíquela como un comentario a la pregunta).
sɐunıɔ ןɐ qɐp
0

Hice algunos cambios para que no necesitemos verificar el recuento manualmente, también agregué restricciones para la etiqueta para que nada salga mal, no importa cuán grande sea el mensaje como se muestra a continuación:

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
fuente
0

O, alternativamente, puede usar una biblioteca ligera poco personalizable

SwiftEmptyState

Enes Karaosman
fuente