处理一个空的UITableView。 打印友好的消息(Handling an empty UIT

2019-08-18 16:21发布

我有一个UITableView,在某些情况下,它是合法的,是空的。 因此,而不是显示应用程序的背景图像,我宁愿在屏幕打印友好的消息,如:

这份名单现在是空的

什么是做到这一点的最简单的方法?

Answer 1:

UITableView中的backgroundView财产是你的朋友。

viewDidLoad或任何地方,你reloadData你应该确定是否有你的表是空的或不并用含有一个UILabel一个UIView更新的UITableView的backgroundView财产或只是将其设置为nil。 而已。

当然,可能使UITableView中的数据源完成双重任务,并返回一个特殊的“列表为空”电池,它给我的印象是一个杂牌。 突然numberOfRowsInSection:(NSInteger)section必须计算它没被问,以确保它们是空过其他部分的行数。 您还需要使具有空消息的特殊细胞。 也不要忘记,你需要可能更改单元格的高度,以适应空消息。 这是所有可行的,但它似乎像创可贴的顶部创可贴。



Answer 2:

基于这里的答案,这里是一个快速的I类提出,你可以在你使用上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;
    }
}

在你UITableViewController您可以在调用这个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
    }
}

从一点点的帮助http://www.appcoda.com/pull-to-refresh-uitableview-empty/



Answer 3:

同Jhonston的答案,但我更喜欢它作为一个扩展:

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

用法:

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
}


Answer 4:

我推荐以下库: DZNEmptyDataSet

添加在您的项目最简单的方法就是用像这样Cocaopods使用它: pod 'DZNEmptyDataSet'

在您的TableViewController添加以下import语句(SWIFT):

import DZNEmptyDataSet

然后确保你的类符合DNZEmptyDataSetSourceDZNEmptyDataSetDelegate像这样:

class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate

在您的viewDidLoad添加以下代码行:

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

现在,所有你需要做的,以显示emptystate是:

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

这些方法是不是强制性的,它也可能只是表明空状态没有按钮等。

对于斯威夫特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)
}


Answer 5:

这样做会被修改数据源返回的一个方法1时的行数是零,并产生在一个专用小区(可能具有不同的小区标识符) tableView:cellForRowAtIndexPath:方法。

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

这可能会有些复杂,如果你有,你需要维护多个表视图控制器,因为有人会忘记插入零检查。 更好的方法是创建一个单独实施的UITableViewDataSource实现,总是返回单行有可配置消息(姑且称之为EmptyTableViewDataSource )。 当由你的表视图控制器改变管理数据,管理的变化,如果数据为空,会检查代码。 如果不是空的,其定期的数据源设置你的表视图控制器; 否则,与所述的一个实例设置EmptyTableViewDataSource已被配置为与相应的消息。



Answer 6:

我一直在使用这个titleForFooterInSection消息。 我不知道这是次优的或没有,但它的作品。

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


Answer 7:

我只能推荐拖动及细胞后掉落的TableView内一个UITextView。 使到ViewController的连接和隐藏/适当时显示它(例如每当表重新加载)。



Answer 8:

因此,对于一个更安全的解决方案:

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

UICollectionView还有:

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

通用的解决方案:

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


Answer 9:

使用backgroundView是好的,但它并不像Mail.app很好地滚动。

我做了类似的事如何xtravar一样。

我加入的视图层次之外的视图tableViewController

然后我用下面的代码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

基本上我加入emptyStateView作为的子视图tableView对象。 作为隔板将重叠的观点,我设置它们的颜色来clearColor 。 要返回到默认的分隔符颜色,你可以将其设置为nil



Answer 10:

使用容器视图控制器是根据做的正确方法苹果 。

我放在一个单独的故事板所有的空状态的看法。 在每一个它自己的UIViewController子类。 我直接在其根视图下添加内容。 如果需要采取任何行动/按钮,你现在已经有一个控制器来处理它。
然后,它只是实例从故事板所需的视图控制器的问题,将其添加为子视图控制器和容器视图添加到的tableView的层次结构(子视图)。 你的空状态视图会滚动的为好,这感觉很好,让你实现拉刷新。

阅读一章“将一个子视图控制器到您的内容”关于如何实现帮助。

只要确保你设置的子视图帧(0, 0, tableView.frame.width, tableView.frame.height)和东西会为中心,正确对齐。



Answer 11:

首先,与其他流行的方法的问题。

BackgroundView

如果你使用将其设定为一个UILabel的简单情况背景视图不能很好地中心。

细胞,页眉,页脚或显示该消息

这干扰了你的功能代码,并介绍了怪异的边缘情况。 如果你想完全居中你的消息,这增加了复杂性的另一个层面。

滚动自己的表视图控制器

你失去了内置功能,如refreshControl,并重新发明轮子。 坚持的UITableViewController最好维护的结果。

添加的UITableViewController作为一个子视图控制器

我有一种感觉,你会在iOS的7+ contentInset问题结束 - 加为什么事情复杂化?

我的解决办法

最好的解决办法,我想出了(和,当然,这是不理想)是使能坐在一个滚动视图的顶部,并采取相应的行动特别景致。 这显然变得复杂,在iOS的7 contentInset疯狂,但它是可行的。

事情你必须要注意:

  • 表分离得到reloadData时带到前面在某些时候 - 你需要警惕的是
  • contentInset / contentOffset - 观察你的特殊鉴于这些键
  • 键盘 - 如果你不想在键盘的方式来获得,这是另一种计算
  • 自动版式 - 你可以不依赖于帧之间的变化来定位你的看法

一旦你有这样一个UIView子类一旦想通了,你可以用它的一切 - 装载纺纱,禁用的观点,显示错误信息,等等。



Answer 12:

这是最好的和简单的解决方案。

    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;


Answer 13:

显示信息为空单,阉了的UITableViewUICollectionView。

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

用途:

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

要么:

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

记住:不要忘记删除消息标签的情况下,数据会刷新之后。



Answer 14:

在故事板中选择您tableviewController场景

拖放的UIView添加标签与你的消息(如:无数据)

创建UIView的出口(说的如yournoDataView)在您的TableViewController。

在viewDidLoad中

self.tableView.backgroundView = yourNoDataView



Answer 15:

斯威夫特的版本,但更好和更简单的形式。 ** 3.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;
        }


Answer 16:

也许不是最好的解决办法,但我刚好把一个标签,在我的桌子的底部,如果行= 0,那么我给你一些文本这样做。 很简单,并达到你正在尝试用几行代码的事情。

我有两个部分在我的表(工作和学校)

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

    if (jobs.count == 0 && schools.count == 0) {
        emptyLbl.text = "No jobs or schools"
    } else {
        emptyLbl.text = ""
    }


Answer 17:

要做到这一点,最简单快捷的方法是拖动标签上侧板的tableView下。 创建标签和一个的tableView出口,并添加一个if语句来隐藏和显示标签和表需要。 或者,也可以添加tableView.tableFooterView = UIView的(帧:CGRect.zero)这对你viewDidLoad中(),得到一个空表的感知如果表和背景视图具有相同的颜色,它是隐藏的。



Answer 18:

使用雨燕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
}


文章来源: Handling an empty UITableView. Print a friendly message