Obsługa pustego UITableView. Wydrukuj przyjazną wiadomość

124

Mam UITableView, który w niektórych przypadkach może być pusty. Zamiast więc wyświetlać obraz tła aplikacji, wolałbym wydrukować przyjazny komunikat na ekranie, taki jak:

Ta lista jest teraz pusta

Jaki jest najprostszy sposób na zrobienie tego?

cateof
źródło
10
Sprawdź ten projekt: github.com/dzenbot/DZNEmptyDataSet
oleynikd
@oleynikd Dziękuję !!!!!
Luda

Odpowiedzi:

173

Właściwość backgroundView UITableView jest Twoim przyjacielem.

W viewDidLoadlub w dowolnym miejscu, w którym reloadDatapowinieneś określić, czy twoja tabela jest pusta, czy nie, i zaktualizuj właściwość backgroundView UITableView za pomocą UIView zawierającego UILabel lub po prostu ustaw ją na zero. Otóż ​​to.

Jest oczywiście możliwe, aby źródło danych UITableView pełniło podwójną funkcję i zwracało specjalną komórkę „lista jest pusta”, co wydaje mi się niezdarne. Nagle numberOfRowsInSection:(NSInteger)sectionmusi obliczyć liczbę wierszy innych sekcji, o które nie pytano, aby upewnić się, że są również puste. Musisz także utworzyć specjalną komórkę, która ma pustą wiadomość. Nie zapominaj również, że prawdopodobnie musisz zmienić wysokość komórki, aby pomieścić pustą wiadomość. To wszystko jest wykonalne, ale wydaje się, że oprócz pomocy bandażowej.

Ray Fix
źródło
12
Myślę, że backgroundView jest najlepszym rozwiązaniem. Dziękuję Ci!
Ferran Maylinch
1
Jedną z wad jest to, że jeśli widok tabeli jest opuszczony, komunikat pozostaje na swoim miejscu. Wolę mieć podobną aplikację w sklepie z aplikacjami pod aktualizacjami. Tutaj pusta wiadomość idzie w parze z przewijaniem ...
testowanie
@testing oczywiście, jeśli potrzebujesz pustego komunikatu do przewijania, nie możesz użyć widoku w tle, ponieważ nie jest to część hierarchii przewijania. Prawdopodobnie chcesz użyć sekcji niestandardowej i UITableViewCell dla stanu pustego.
LightningStryk
Przełącz backgroundViewukrytą właściwość jest drogą do zrobienia. Z DZNEmptyDataSet, musimy użyć jegoemptyDataSetSource
onmyway133
Jeśli chcesz dodać przyciski, musisz to zrobić: tableView.backgroundView!.userInteraction = truepo ustawieniu linii tableView.backgroundView = constructMyViewWithButtons()lub w inny sposób.
kbpontius
90

To samo, co odpowiedź Jhonstona, ale wolałem to jako rozszerzenie:

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

Stosowanie:

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
źródło
Dziękuje za twoją sugestię.
ssowri1
1
Rozszerzenie jest lepsze! Dzięki.
Ogulcan Orhan,
Uwielbiam rozszerzenie :)
Alix
1
Jeśli masz sekcje, następnie trzeba przenieść tę logikę do func numberOfSections(in tableView: UITableView) -> Intmetody
rzemiosła
87

W oparciu o odpowiedzi tutaj, oto krótkie zajęcia, które stworzyłem, z których możesz korzystać w swoim 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;
    }
}

W swoim UITableViewControllermożesz to wywołaćnumberOfSectionsInTableView(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
    }
}

wprowadź opis obrazu tutaj

Z niewielką pomocą http://www.appcoda.com/pull-to-refresh-uitableview-empty/

Johnston
źródło
chciałbym jednak dodać do niego obraz. Ale świetne szybkie rozwiązanie.
AnBisw
10
użyj rozszerzenia zamiast klasy (nie przekazuj kontrolera widoku)
Cesare
Nie udało mi się sprawić, aby powyższy kod działał dla mnie, ale udało mi się uzyskać kod w tej odpowiedzi (pierwszej), aby działał, na wypadek, gdyby inni mieli ten sam problem. Myślę też, że druga odpowiedź również by działała (używając stacji dokującej sceny) - stackoverflow.com/questions/28532926/…
Renee Olson
viewController.tableView.separatorStyle = .nonenie jest wymagane.
Dmitry
17

Polecam bibliotekę: DZNEmptyDataSet

Najłatwiejszym sposobem dodania go do projektu jest użycie go z Cocaopodami w następujący sposób: pod 'DZNEmptyDataSet'

W swoim TableViewController dodaj następującą instrukcję importu (Swift):

import DZNEmptyDataSet

Następnie upewnij się, że klasa jest zgodna ze DNZEmptyDataSetSourcei DZNEmptyDataSetDelegatetak:

class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate

W twojej viewDidLoaddodać następujące wiersze kodu:

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

Teraz wszystko, co musisz zrobić, aby wyświetlić pusty stan, to:

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

Te metody nie są obowiązkowe, można też po prostu pokazać stan pusty bez przycisku itp.

Dla 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
źródło
Mam problemy z nieprawidłowym wyrównaniem w pionie DZEmptyDataSet. Czy ktoś miał ten sam problem?
amariduran
12

Jednym ze sposobów byłoby zmodyfikowanie źródła danych tak, aby zwracało się, 1gdy liczba wierszy wynosi zero, i utworzenie w tableView:cellForRowAtIndexPath:metodzie komórki specjalnego przeznaczenia (być może z innym identyfikatorem komórki) .

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

Może się to nieco skomplikować, jeśli masz wiele kontrolerów widoku tabeli, które musisz utrzymywać, ponieważ ktoś w końcu zapomni wstawić zerową kontrolę. Lepszym podejściem jest utworzenie osobnej implementacji UITableViewDataSourceimplementacji, która zawsze zwraca pojedynczy wiersz z konfigurowalnym komunikatem (nazwijmy to EmptyTableViewDataSource). Gdy dane zarządzane przez kontroler widoku tabeli ulegną zmianie, kod zarządzający zmianą sprawdzi, czy dane są puste. Jeśli nie jest pusty, ustaw kontroler widoku tabeli na jego zwykłe źródło danych; w przeciwnym razie ustaw go za pomocą instancji EmptyTableViewDataSource, która została skonfigurowana za pomocą odpowiedniego komunikatu.

dasblinkenlight
źródło
5
Miałem problemy z zrobieniem tego z tabelami, w których usunięto wiersze z, deleteRowsAtIndexPaths:withRowAnimation:ponieważ liczba zwrócona z numberOfRowsInSectionmusi pasować do wyniku $ numRows - $ rowsDeleted.
Animal451
4
Bardzo, bardzo odradzam. Zagłębia twój kod w skrajne przypadki.
xtravar
2
@xtravar Zamiast głosować w dół, zastanów się nad opublikowaniem własnej odpowiedzi.
dasblinkenlight
1
Podążałem tą ścieżką i prowadzi to do własnego szeregu problemów. Z mojego doświadczenia wynika, że ​​prawie zawsze lepiej jest rozszerzyć interfejsy API Apple niż próbować je zastąpić.
xtravar
1
To rozwiązanie nie będzie działać z NSFetchedResultsController. Zamiast tego spróbuję podejścia backgroundView.
Sam,
10

W tym celu używam wiadomości titleForFooterInSection. Nie wiem, czy jest to nieoptymalne, czy nie, ale działa.

-(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.
źródło
2
Niezła sztuczka i wydaje mi się całkiem czysta. Oto jedna linijka:return tableView.numberOfRowsInSection(section) == 0 ? "This list is now empty" : nil
TruMan1
8

Więc dla bezpieczniejszego rozwiązania:

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

a UICollectionViewtakże:

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

Bardziej ogólne rozwiązanie:

    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
źródło
7

Mogę tylko polecić przeciągnięcie i upuszczenie UITextView wewnątrz TableView po komórkach. Nawiąż połączenie z ViewController i ukryj / wyświetl go w razie potrzeby (np. Za każdym razem, gdy tabela zostanie ponownie załadowana).

wprowadź opis obrazu tutaj

pasql
źródło
To najłatwiejszy sposób)!
moonvader
Taki bolesny ból głowy po jabłku; dzięki za ten pomysł
Eenvincible
5

Korzystanie z backgroundView jest w porządku, ale nie przewija się tak dobrze, jak w Mail.app.

Zrobiłem coś podobnego do tego, co zrobił xtravar .

Dodałem widok poza hierarchią widoków tableViewController. hierarchia

Następnie użyłem następującego kodu w 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

Zasadniczo dodałem emptyStateViewjako podwidok tableViewobiektu. Ponieważ separatory zachodziłyby na widok, ustawiłem ich kolor na clearColor. Aby powrócić do domyślnego koloru separatora, możesz po prostu ustawić go na nil.

Mangerlahn
źródło
To działa całkiem nieźle! Ale jeśli masz elementy do ściągnięcia do odświeżenia, np. To nie przewija się razem z nimi. Uważam, że lepiej jest ustawić tableHeaderViewi upewnić się, że dopasowuje się do rozmiaru .
Hannele
4

Według Apple używanie kontrolera widoku kontenera jest właściwym sposobem na zrobienie tego .

Umieściłem wszystkie moje puste widoki stanu w oddzielnym Storyboard. Każdy w swojej własnej podklasie UIViewController. Treść dodaję bezpośrednio pod ich głównym widokiem. Jeśli potrzebna jest jakakolwiek akcja / przycisk, masz już kontroler do jej obsługi.
Następnie wystarczy utworzyć wystąpienie żądanego kontrolera widoku z tego Storyboard, dodać go jako kontroler widoku podrzędnego i dodać widok kontenera do hierarchii tableView (widok podrzędny). Twój pusty widok stanu będzie również przewijalny, co jest przyjemne i pozwala na zaimplementowanie przeciągania w celu odświeżenia.

Przeczytaj rozdział „Dodawanie kontrolera widoku podrzędnego do treści”, aby uzyskać pomoc dotyczącą implementacji.

Po prostu upewnij się, że podrzędna ramka widoku jest ustawiona jako, (0, 0, tableView.frame.width, tableView.frame.height)a elementy zostaną odpowiednio wyśrodkowane i wyrównane.

AmitP
źródło
3

Po pierwsze, problemy z innymi popularnymi podejściami.

BackgroundView

Widok tła nie jest ładnie wyśrodkowany, jeśli użyjesz prostego przypadku ustawienia go jako UILabel.

Komórki, nagłówki lub stopki do wyświetlenia wiadomości

To zakłóca twój kod funkcjonalny i wprowadza dziwne przypadki skrajne. Jeśli chcesz idealnie wyśrodkować swój przekaz, dodaje to kolejnego poziomu złożoności.

Rolling własnego kontrolera widoku tabeli

Tracisz wbudowane funkcje, takie jak refreshControl, i ponownie wymyślasz koło. Trzymaj się UITableViewController, aby uzyskać najlepsze możliwe do utrzymania wyniki.

Dodawanie UITableViewController jako kontrolera widoku podrzędnego

Mam przeczucie, że skończysz z problemami z zawartością w iOS 7+ - plus po co komplikować sprawy?

Moje rozwiązanie

Najlepszym rozwiązaniem, które wymyśliłem (i oczywiście nie jest to idealne rozwiązanie) jest utworzenie specjalnego widoku, który można umieścić na widoku przewijania i odpowiednio działać. To oczywiście komplikuje się w iOS 7 z szaleństwem contentInset, ale jest to wykonalne.

Na co musisz uważać:

  • separatory tabeli wysuwają się na przód w pewnym momencie podczas reloadData - musisz się przed tym strzec
  • contentInset / contentOffset - obserwuj te klawisze w swoim specjalnym widoku
  • klawiatura - jeśli nie chcesz, aby klawiatura przeszkadzała, to kolejna kalkulacja
  • autolayout - nie możesz polegać na zmianach klatek, aby ustawić swój widok

Gdy już raz to zrozumiesz w jednej podklasie UIView, możesz jej użyć do wszystkiego - ładowania spinnerów, wyłączania widoków, wyświetlania komunikatów o błędach itp.

xtravar
źródło
Czy możesz rozwinąć swoją odpowiedź. Po czym poznać, że stół jest pusty? Aby następnie „działać odpowiednio”.
Michael Ozeryansky
Wiesz, że twoja tabela jest pusta, ponieważ to ty ją wypełniasz. Więc w kontrolerze widoku miałbyś asynchroniczne wywołanie zwrotne ładowania. W tym wywołaniu zwrotnym, jeśli (itemsToShow.count == 0) {dodaj swój widok do widoku przewijania}. Najtrudniejsze jest ustawienie tego widoku z góry („tarcza / widok wiadomości”) tak, aby zajmował całą klatkę widoku przewijania, bez zawartości elementu contentInset itp., Tak aby wiadomość była wyśrodkowana w pionie.
xtravar
Jak dodać widok u góry widoku tabeli? self.view to widok tabeli i jeśli używam addSubView, jest dołączony do widoku tabeli, co zawsze powoduje problemy z automatycznym układem.
testowanie
Widok, który umieścisz w widoku tabeli, nie powinien używać autoukładu, a sam widok tabeli nie powinien używać autoukładu.
xtravar
1
Przegapiłeś tam jedno podejście; Możesz użyć UITableViewController i dodać go jako podrzędny kontroler widoku do zwykłego UIViewController
user1169629
3

To najlepsze i najprostsze rozwiązanie.

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
źródło
2

Pokaż wiadomość dla pustej listy, Wether jej UITableView lub 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
        }
    }
}

Zastosowania:

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

LUB:

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

Pamiętaj: nie zapomnij usunąć etykiety wiadomości na wypadek, gdyby dane pojawiały się po odświeżeniu.

Gurjit Singh
źródło
1
Dodałbym warunek if if messages.count! = 0 {show}
Eddwin Paz
1

Wybierz scenę tableviewController w serii ujęć

wprowadź opis obrazu tutaj

Przeciągnij i upuść UIView Dodaj etykietę wraz z wiadomością (np .: Brak danych)

wprowadź opis obrazu tutaj

utwórz wylot UIView (powiedzmy na przykład yournoDataView) na swoim TableViewController.

i in viewDidLoad

self.tableView.backgroundView = yourNoDataView

gaurav bhardwaj
źródło
1

Korzystanie ze 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
źródło
1

Możesz dodać to do swojej klasy podstawowej.

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

Pokaż to w ten sposób w klasie na temat pobierania danych z interfejsu API.

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

Ukryj to w ten sposób.

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

func hideNoDataMessage(){
    messageLabel.removeFromSuperview()
}
Mudassir Asghar
źródło
Hej @ Mudassir-Asghar, jak wygląda funkcja hideNoDataMessage?
Saamer
1
Cześć, @Saamer Właśnie zaktualizowałem powyższą odpowiedź. dzięki za wskazanie tego, mam nadzieję, że to pomoże :)
Mudassir Asghar
0

Szybka wersja, ale lepsza i prostsza forma. ** 3.0

Mam nadzieję, że spełni Twoje cele ......

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

Klasa pomocnicza z funkcją:

 /* 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
źródło
0

Prawdopodobnie nie jest to najlepsze rozwiązanie, ale zrobiłem to, po prostu umieszczając etykietę na dole mojej tabeli i jeśli wiersze = 0, przypisuję jej jakiś tekst. Całkiem proste i osiąga to, co próbujesz zrobić, za pomocą kilku wierszy kodu.

Mam dwie sekcje w mojej tabeli (praca i szkoły)

    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
źródło
Jeśli chcesz, możesz również zastąpić etykietę pustym widokiem obrazu, gdy count = 0 ...
Zach,
0

Najłatwiejszym i najszybszym sposobem na to jest przeciągnięcie etykiety na panel boczny pod tableView. Utwórz wylot dla etykiety i tableView i dodaj instrukcję if, aby ukryć i wyświetlić etykietę i tabelę w razie potrzeby. Alternatywnie możesz dodać tableView.tableFooterView = UIView (frame: CGRect.zero) to do ciebie viewDidLoad (), aby nadać pustej tabeli wrażenie, że jest ona ukryta, jeśli tabela i widok tła mają ten sam kolor.

yoamod
źródło
(Wydaje się, że ten post nie zapewnia dobrej odpowiedzi na pytanie. Zmień odpowiedź i uzupełnij ją bardziej szczegółowymi informacjami lub po prostu opublikuj jako komentarz do pytania).
sɐunıɔ ןɐ qɐp
0

Zrobiłem kilka zmian, żebyśmy nie musieli ręcznie sprawdzać licznika, dodałem również ograniczenia dla etykiety, aby nic nie poszło źle, niezależnie od tego, jak duży jest komunikat, jak pokazano poniżej:

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

Stosowanie

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let message: String = "The list is empty."
        ticketsTableView.setEmptyMessage(message, tickets.isEmpty)
        return self.tickets.count
    }
Kompilator Alsh
źródło