Если результатов просмотра таблицы нет, отобразите на экране «Нет результатов»

114

У меня есть tableview, где иногда может не быть результатов для перечисления, поэтому я хотел бы разместить что-то, что говорит «нет результатов», если нет результатов (либо метка, либо одна ячейка представления таблицы?).

Есть ли самый простой способ сделать это?

Я бы попробовать labelза tableviewто спрятать один из двух на основе результатов, но так как я работаю с TableViewControllerи не нормальный , ViewControllerя не знаю , как умный или выполнимо , что есть.

Я также использую Parseи подклассифицирую как PFQueryTableViewController:

@interface TableViewController : PFQueryTableViewController

Я могу предоставить любую дополнительную информацию, просто дайте мне знать!

TableViewController Сцена в раскадровке:

введите описание изображения здесь

РЕДАКТИРОВАТЬ: Per Midhun MP, вот код, который я использую

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSInteger numOfSections = 0;
    if ([self.stringArray count] > 0)
    {
        self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
        numOfSections                 = 1;
        //yourTableView.backgroundView   = nil;
        self.tableView.backgroundView = nil;
    }
    else
    {
        UILabel *noDataLabel         = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height)];
        noDataLabel.text             = @"No data available";
        noDataLabel.textColor        = [UIColor blackColor];
        noDataLabel.textAlignment    = NSTextAlignmentCenter;
        //yourTableView.backgroundView = noDataLabel;
        //yourTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        self.tableView.backgroundView = noDataLabel;
        self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }

    return numOfSections;
}

И вот вид, который я получаю, в нем все еще есть разделительные линии. У меня такое ощущение, что это небольшое изменение, но я не уверен, почему появляются разделительные линии?

введите описание изображения здесь

SRMR
источник
Я думаю, что уловка заключалась в том, чтобы настроить пустой footerView на tableview, чтобы избавиться от строк.
Бен Синклер,
@ Энди Что ты имеешь в виду?
SRMR
Не используйте решение, добавленное к этому вопросу. Методы источника данных табличного представления никогда не должны делать больше, чем они должны делать. numberOfSectionsдолжен вернуть счет, и все. То же самое для numberOfRowsInSection. Их можно вызывать много раз в любое время. Никогда не обновляйте представления, не обновляйте данные и не делайте ничего, кроме возврата счетчика. Логика обновления представлений никогда не должна быть в этих методах.
rmaddy

Ответы:

206

Вы можете легко добиться этого, используя backgroundViewсвойство UITableView.

Цель C:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSInteger numOfSections = 0;
    if (youHaveData)
    {
        yourTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
        numOfSections                = 1;
        yourTableView.backgroundView = nil;
    }
    else
    {   
        UILabel *noDataLabel         = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, yourTableView.bounds.size.width, yourTableView.bounds.size.height)];
        noDataLabel.text             = @"No data available";
        noDataLabel.textColor        = [UIColor blackColor];
        noDataLabel.textAlignment    = NSTextAlignmentCenter;
        yourTableView.backgroundView = noDataLabel;
        yourTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }

    return numOfSections;
}

Swift:

func numberOfSections(in tableView: UITableView) -> Int
{
    var numOfSections: Int = 0
    if youHaveData
    {
        tableView.separatorStyle = .singleLine
        numOfSections            = 1
        tableView.backgroundView = nil
    }
    else
    {
        let noDataLabel: UILabel  = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
        noDataLabel.text          = "No data available"
        noDataLabel.textColor     = UIColor.black
        noDataLabel.textAlignment = .center
        tableView.backgroundView  = noDataLabel
        tableView.separatorStyle  = .none
    }
    return numOfSections
}

Ссылка Ссылка на класс UITableView

backgroundView Свойство

Фоновый вид таблицы.

Декларация

Swift

var backgroundView: UIView?

Objective-C

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

Обсуждение

Размер фона представления таблицы автоматически изменяется в соответствии с размером представления таблицы. Это представление размещается как часть представления таблицы за всеми ячейками, представлениями заголовков и нижних колонтитулов.

Вы должны установить для этого свойства значение nil, чтобы установить цвет фона представления таблицы.

Midhun MP
источник
Спасибо за ответ! Значит ли это, что мне нужна tableViewрозетка, чтобы я мог делать это yourTableView.backgroundView? Я использую, TableViewControllerпоэтому у меня сейчас нет выхода для него, вот почему мне интересно.
SRMR
1
UITableViewControllerуже есть свойство с именем, tableViewпоэтому вам не нужно его создавать
Cihan Tek
Понял, используюself.tableView
SRMR
1
Коррекция. При дальнейшем тестировании мне действительно пришлось заставить указанную мной строку возвращать то же значение. numOfSections = 1. Если я сделаю его нулевым и мое табличное представление будет пустым, оно будет работать, но когда я создаю ячейку, а затем удаляю ее, вместо удаления и отображения сообщения происходит сбой со следующей ошибкой. *** Завершение работы приложения из-за неперехваченного исключения «NSInternalInconsistencyException», причина: «Внутренняя ошибка UITableView: невозможно создать новую карту разделов со старым количеством разделов: 1 и новым количеством разделов: 0». Я не знаю, почему возвращается одно и то же значение, но оно работает и работает так, как должно.
ChrisOSX
2
Не используйте это решение. Методы источника данных табличного представления никогда не должны делать больше, чем они должны делать. numberOfSectionsдолжен вернуть счет, и все. То же самое для numberOfRowsInSection. Их можно вызывать много раз в любое время. Никогда не обновляйте представления, не обновляйте данные и не делайте ничего, кроме возврата счетчика. Логика обновления представлений никогда не должна быть в этих методах.
rmaddy
107

Для Xcode 8.3.2 - Swift 3.1

Вот не очень известный, но невероятно простой способ добавления представления «Без элементов» к представлению пустой таблицы, которое восходит к Xcode 7. Я оставлю его вам управлять той логикой, которая добавляет / удаляет в фоновое представление таблицы, но вот последовательность действий для раскадровки и Xcode (8.3.2):

  1. Выберите сцену в раскадровке, в которой есть вид таблицы.
  2. Перетащите пустой UIView в «Док сцены» этой сцены.

введите описание изображения здесь

  1. Добавьте UILabel и любые ограничения в новое представление, а затем создайте IBOutlet для этого представления.

введите описание изображения здесь

  1. Назначьте это представление tableView.backgroundView

введите описание изображения здесь

  1. Узрите волшебство!

введите описание изображения здесь

В конечном итоге это работает в любое время, когда вы хотите добавить простое представление к своему контроллеру представления, которое не обязательно должно отображаться немедленно, но которое вы также не хотите передавать код.

Поль Бонневиль
источник
1
Этот метод работает для всего, для чего вам нужен простой вид. Я также использовал его для пользовательских заголовков разделов таблицы. На самом деле, вы можете использовать его для всего, для чего вам нужен вид. Просто сделайте ссылку через IBOutlet и добавьте его как подпредставление в любое место, где вам нужно настраиваемое представление.
Поль Бонневиль
Как сказал @gmogames, после 2 лет работы с IOS, это первый раз, когда я узнаю о фоне tableView! спасибо человек
Али Адиль
1
Спасибо за это. Чтобы разместить это там, мне пришлось использовать tableView.separatorStyle = .none и изменить размер UIView по умолчанию, чтобы увидеть backgroundView. Я использую Xcode 9.4 со Swift 4.
Хаммад Тарик
Как разместить настраиваемый вид?
Nol4635 02
1
@ Nol4635 В случае фона табличного представления вы просто назначаете представление, которое вы создали как IBOutlet, фону табличного представления или, если вы просто хотите добавить представление к любому другому представлению, вы можете просто добавить его как подпредставление. Вам нужно будет установить значения кадра, но это похоже на любое другое представление.
Поль Бонневиль
30

Быстрая версия приведенного выше кода: -

func numberOfSectionsInTableView(tableView: UITableView) -> Int {

    var numOfSection: NSInteger = 0

    if CCompanyLogoImage.count > 0 {

        self.tableView.backgroundView = nil
        numOfSection = 1


    } else {

        var noDataLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
        noDataLabel.text = "No Data Available"
        noDataLabel.textColor = UIColor(red: 22.0/255.0, green: 106.0/255.0, blue: 176.0/255.0, alpha: 1.0)
        noDataLabel.textAlignment = NSTextAlignment.Center
        self.tableView.backgroundView = noDataLabel

    }
    return numOfSection
}

Но если вы загружаете информацию из JSON, вам нужно проверить, является ли JSON пустым или нет, поэтому, если вы поместите такой код, он сначала покажет сообщение «Нет данных», а затем исчезнет. Потому что после перезагрузки таблицы данные скрываются. Таким образом, вы можете поместить этот код в место загрузки данных JSON в массив. ТАК :-

func numberOfSectionsInTableView(tableView: UITableView) -> Int {

    return 1
}

func extract_json(data:NSData) {


    var error: NSError?

    let jsonData: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers , error: &error)
    if (error == nil) {
        if let jobs_list = jsonData as? NSArray
        {
            if jobs_list.count == 0 {

                var noDataLabel: UILabel = UILabel(frame: CGRectMake(0, 0, self.tableView.bounds.size.width, self.tableView.bounds.size.height))
                noDataLabel.text = "No Jobs Available"
                noDataLabel.textColor = UIColor(red: 22.0/255.0, green: 106.0/255.0, blue: 176.0/255.0, alpha: 1.0)
                noDataLabel.textAlignment = NSTextAlignment.Center
                self.tableView.backgroundView = noDataLabel

            }

            for (var i = 0; i < jobs_list.count ; i++ )
            {
                if let jobs_obj = jobs_list[i] as? NSDictionary
                {
                    if let vacancy_title = jobs_obj["VacancyTitle"] as? String
                    {
                        CJobTitle.append(vacancy_title)

                        if let vacancy_job_type = jobs_obj["VacancyJobType"] as? String
                        {
                            CJobType.append(vacancy_job_type)

                            if let company_name = jobs_obj["EmployerCompanyName"] as? String
                            {
                                CCompany.append(company_name)

                                    if let company_logo_url = jobs_obj["EmployerCompanyLogo"] as? String
                                    {
                                        //CCompanyLogo.append("http://google.com" + company_logo_url)

                                        let url = NSURL(string: "http://google.com" + company_logo_url )
                                        let data = NSData(contentsOfURL:url!)
                                        if data != nil {
                                            CCompanyLogoImage.append(UIImage(data: data!)!)
                                        }

                                        if let vacancy_id = jobs_obj["VacancyID"] as? String
                                        {
                                            CVacancyId.append(vacancy_id)

                                        }

                                    }

                            }

                        }
                    }
                }
            }
        }
    }
    do_table_refresh();


}

func do_table_refresh() {

    dispatch_async(dispatch_get_main_queue(), {
        self.tableView.reloadData()
        return
    })
}
Чатуранга Силва
источник
Это очень хорошо работает для отображения метки, когда в строках нет данных. Однако как лучше всего поступать, когда данные поступают и self.tableView.reloadData()вызываются? Я обнаружил, что он добавляет мои новые строки, но noDataLabelпод ними все еще отображается.
tylerSF
1
Если в вашем массиве есть элемент, просто установите self.tableView.backgroundView = nil
Чатуранга Сильва
if jobs_list.count == 0 {// добавить указанный выше код} else {self.tableView.backgroundView = nil}
Чатуранга Сильва,
идеальный. добавление self.tableView.backgroundView = nilв мой оператор if раньше return jobs_list.count работает хорошо. спасибо
tylerSF
Не используйте в этом ответе первое решение. Методы источника данных табличного представления никогда не должны делать больше, чем они должны делать. numberOfSectionsдолжен вернуть счет, и все. То же самое для numberOfRowsInSection. Их можно вызывать много раз в любое время. Никогда не обновляйте представления, не обновляйте данные и не делайте ничего, кроме возврата счетчика. Логика обновления представлений никогда не должна быть в этих методах.
rmaddy
6

Вы можете попробовать этот элемент управления. Это довольно аккуратно. DZNEmptyDataSet

Или на вашем месте все, что я сделал бы, это

  • Проверьте, пуст ли ваш массив данных
  • Если он пуст, добавьте к нему один объект с именем @ "Нет данных".
  • Отобразить эту строку в cell.textLabel.text

Очень просто

Сэм Б
источник
5

Swift 3 (обновлено):

override func numberOfSections(in tableView: UITableView) -> Int {
    if myArray.count > 0 {
        self.tableView.backgroundView = nil
        self.tableView.separatorStyle = .singleLine
        return 1
    }

    let rect = CGRect(x: 0,
                      y: 0,
                      width: self.tableView.bounds.size.width,
                      height: self.tableView.bounds.size.height)
    let noDataLabel: UILabel = UILabel(frame: rect)

    noDataLabel.text = "Custom message."
    noDataLabel.textColor = UIColor.white
    noDataLabel.textAlignment = NSTextAlignment.center
    self.tableView.backgroundView = noDataLabel
    self.tableView.separatorStyle = .none

    return 0
}
Теодор Чурару
источник
Не используйте это решение. Методы источника данных табличного представления никогда не должны делать больше, чем они должны делать. numberOfSectionsдолжен вернуть счет, и все. То же самое для numberOfRowsInSection. Их можно вызывать много раз в любое время. Никогда не обновляйте представления, не обновляйте данные и не делайте ничего, кроме возврата счетчика. Логика обновления представлений никогда не должна быть в этих методах.
rmaddy
Я согласен. Это решение можно улучшить, но его тоже можно использовать.
Теодор Чурару
Как вы можете согласиться с тем, что вам не следует использовать это решение, но затем заявите, что его также можно использовать? Это противоречие.
rmaddy
Я согласен с тем, что это решение можно улучшить, но в его текущем состоянии оно также будет работать.
Теодор Чурару
5

Swift3.0

Я надеюсь, что это сервер вашей цели ...... В вашем 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
        }
    }
}

Класс-помощник с функцией:

 /* 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;
        }
Равиндра Шекхават
источник
Не используйте это решение. Методы источника данных табличного представления никогда не должны делать больше, чем они должны делать. numberOfSectionsдолжен вернуть счет, и все. То же самое для numberOfRowsInSection. Их можно вызывать много раз в любое время. Никогда не обновляйте представления, не обновляйте данные и не делайте ничего, кроме возврата счетчика. Логика обновления представлений никогда не должна быть в этих методах.
rmaddy
3

Я думаю, что самый элегантный способ решить вашу проблему - это переключиться с a UITableViewControllerна a, UIViewControllerкоторое содержит UITableView. Таким образом, вы можете добавить все, UIViewчто хотите, в качестве подвидов основного представления.

Я бы не рекомендовал использовать для этого UITableViewCell, вам может потребоваться добавить дополнительные вещи в будущем, и все может быстро стать некрасивым.

Вы также можете сделать что-то подобное, но это тоже не лучшее решение.

UIWindow* window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: OverlayView];
Джихан Тек
источник
Спасибо за цвет, который является наиболее элегантным способом реализации, всегда хороший способ подумать об этом. Я могу попробовать изменить раскадровку, чтобы использовать файл, UIViewControllerсодержащий UITableView.
SRMR
1
Это наиболее перспективный способ. Я сам никогда не использовал его UITableViewController, потому что использовать его не намного проще, чем делать наоборот, но он ограничивает вашу способность что-то менять в будущем.
Cihan Tek
Это не гарантия будущего, если у вас полупрозрачные стержни. Я настоятельно не рекомендую. По крайней мере, создайте UIViewController, который содержит UITableViewController и соответствующим образом настраивает вещи.
xtravar
3

Используйте этот код в своем numberOfSectionsInTableViewметоде: -

if ([array count]==0
{

    UILabel *fromLabel = [[UILabel alloc]initWithFrame:CGRectMake(50, self.view.frame.size.height/2, 300, 60)];                                                                                        
    fromLabel.text =@"No Result";
    fromLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines;
    fromLabel.backgroundColor = [UIColor clearColor];
    fromLabel.textColor = [UIColor lightGrayColor];
    fromLabel.textAlignment = NSTextAlignmentLeft;
    [fromLabel setFont:[UIFont fontWithName:Embrima size:30.0f]];
    [self.view addSubview:fromLabel];
    [self.tblView setHidden:YES];
}
Chandan
источник
1
Не используйте это решение. Методы источника данных табличного представления никогда не должны делать больше, чем они должны делать. numberOfSectionsдолжен вернуть счет, и все. То же самое для numberOfRowsInSection. Их можно вызывать много раз в любое время. Никогда не обновляйте представления, не обновляйте данные и не делайте ничего, кроме возврата счетчика. Логика обновления представлений никогда не должна быть в этих методах.
rmaddy
2

SWIFT 3

        let noDataLabel: UILabel     = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
        noDataLabel.text             = "No data available"
        noDataLabel.textColor        = UIColor.white
        noDataLabel.font             = UIFont(name: "Open Sans", size: 15)
        noDataLabel.textAlignment    = .center
        tableView.backgroundView = noDataLabel
        tableView.separatorStyle = .none
Ахмед Сафади
источник
2

Если вы не используете нижний колонтитул tableview и не хотите, чтобы tableview заполнял экран пустыми ячейками таблицы по умолчанию, я бы посоветовал вам установить нижний колонтитул tableview на пустой UIView. Я не знаю правильного синтаксиса для этого в obj-c или Swift, но в Xamarin.iOS я бы сделал это так:

public class ViewController : UIViewController
{
    UITableView _table;

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

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

        _table.TableFooterView = new UIView();
    }
}

Приведенный выше код приведет к просмотру таблицы без пустых ячеек

frmi
источник
2

Вот решение, которое сработало для меня.

  1. Добавьте следующий код в новый файл.

  2. Измените класс таблицы на пользовательский класс MyTableView из раскадровки или .xib

(это будет работать только для первого раздела. Если вы хотите настроить больше, внесите соответствующие изменения в функцию MyTableView reloadData () для других разделов)

public class MyTableView: UITableView {

    override public func reloadData() {
        super.reloadData()

        if self.numberOfRows(inSection: 0) == 0 {
            if self.viewWithTag(1111) == nil {
                let noDataLabel = UILabel()
                noDataLabel.textAlignment = .center
                noDataLabel.text = "No Data Available"
                noDataLabel.tag = 1111
                noDataLabel.center = self.center
                self.backgroundView = noDataLabel
            }

        } else {
            if self.viewWithTag(1111) != nil {
                self.backgroundView = nil
            }
        }
    }
}
ShrikantWalekar
источник
Это хороший подход. Я добавил несколько улучшений, чтобы сделать его более округлым. 1. Кэшировать стиль разделителя tableview и восстанавливать его при переходе между пустым / непустым состоянием, чтобы сделать этот подкласс применимым для случаев использования, в которых требуются разные стили разделителей. 2. Добавьте @IBInspectable var emptyStateText: String?и отметьте этот класс, так как @IBDesignableтогда вы сможете изменить другой текст пустого состояния для разных сцен в раскадровке или xibs. Наслаждайтесь! :)
Эгист Ли
1

Я бы представил представление наложения, которое имеет вид и сообщение, которое вы хотите, если представление таблицы не имеет результатов. Вы можете сделать это в ViewDidAppear, чтобы у вас были результаты до того, как отображать / не отображать представление.

Сирисс
источник
1

Если вы хотите сделать это без кода, попробуйте это!

Нажмите на свой tableView.

tableView Скриншот

Измените стиль с «простого» на «сгруппированный».

Скриншот свойств табличного представления-2

Теперь, когда вы используете ....

tableView.backgroundView = ВСТАВЬТЕ ЭТИКЕТКУ ИЛИ ПРОСМОТРЕТЬ

Он не покажет разделители!

Ф. Моралес
источник
1

Добавьте этот код в один файл и измените тип коллекции на CustomCollectionView.

import Foundation

class CustomCollectionView: UICollectionView {

  var emptyModel = EmptyMessageModel()
  var emptyView: EmptyMessageView?
  var showEmptyView: Bool = true

  override func reloadData() {
    super.reloadData()

    emptyView?.removeFromSuperview()
    self.backgroundView = nil

    if !showEmptyView {
      return
    }

    if numberOfSections < 1 {
      let rect = CGRect(x: 0,
                        y: 0,
                        width: self.bounds.size.width,
                        height: self.bounds.size.height)

      emptyView = EmptyMessageView()
      emptyView?.frame = rect
      if let emptyView = emptyView {
        //                self.addSubview(emptyView)
        self.backgroundView = emptyView
      }
      emptyView?.setView(with: emptyModel)

    } else {
      emptyView?.removeFromSuperview()
      self.backgroundView = nil
    }
  }
}

class EmptyMessageView: UIView {

  @IBOutlet weak var messageLabel: UILabel!
  @IBOutlet weak var imageView: UIImageView!

  var view: UIView!

  override init(frame: CGRect) {
    super.init(frame: frame)
    xibSetup()
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    xibSetup()
  }

  func xibSetup() {
    view = loadViewFromNib()

    view.frame = bounds

    view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    addSubview(view)
  }

  func loadViewFromNib() -> UIView {

    let bundle = Bundle(for: type(of: self))
    let nib = UINib(nibName: "EmptyMessageView", bundle: bundle)
    let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView

    return view
  }

  func setView(with model: EmptyMessageModel) {
    messageLabel.text = model.message ?? ""
    imageView.image = model.image ?? #imageLiteral(resourceName: "no_notification")
  }
}

///////////
class EmptyMessageModel {
  var message: String?
  var image: UIImage?

  init(message: String = "No data available", image: UIImage = #imageLiteral(resourceName: "no_notification")) {
    self.message = message
    self.image = image
  }
}
ShrikantWalekar
источник