如果没有表格视图结果,屏幕上显示“无结果”。

118

我有一个tableview,有时可能没有任何结果要列出,因此如果没有结果(无论是标签还是一个table视图单元),我想放置一些内容来显示"无结果"

有没有更简单的方法?

我会尝试在tableview后面放一个label,然后根据结果隐藏其中之一,但由于我正在使用PFQueryTableViewController而不是普通的ViewController,所以不确定这是否明智或可行。

我还使用Parse和子类化为PFQueryTableViewController

@interface TableViewController : PFQueryTableViewController

我可以提供任何需要的额外细节,请让我知道!

Storyboard 中的 TableViewController 场景:

在此输入图像描述

编辑: 根据 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;
}

这是我得到的视图,它仍然有分隔线。我觉得这可能是一些小改变,但我不确定为什么会出现分隔线?

enter image description here


我认为这个技巧是在表格视图上设置一个空的footerView以摆脱底部分隔线。 - pronebird
@Andy 你是什么意思? - SRMR
1
表格视图数据源方法不能超出其本身的职能。numberOfSections应该返回计数,就只是这样。numberOfRowsInSection也是一样的。这些方法可以在任何时间被多次调用。除了返回计数以外,不要更新视图或数据,也不要进行其他操作。更新视图的逻辑绝不应该在这些方法中。 - rmaddy
15个回答

216

使用 UITableViewbackgroundView 属性即可轻松实现此目的。

Objective 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 才能设置表视图的背景颜色。


1
UITableViewController 已经有一个名为 tableView 的属性,因此您不需要创建一个。 - Cihan Tek
我仍然得到分隔线,有任何想法是为什么吗?谢谢! - SRMR
@ChrisOSX:我很惊讶为什么这对你有帮助。因为我们将numOfSections初始化为0,如果它不进入if块,那么值就会是0。所以在else中再次将其设置为0并不会有任何区别。我认为你应该检查一下我的先前评论,我认为那才是你崩溃的原因。无论如何,感谢您的评论。 - Midhun MP
1
更正:经过进一步测试,我实际上必须让我提到的那行代码返回相同的值。 numOfSections = 1。如果我将其设置为零并且我的表视图为空,则可以正常工作,但是当我创建一个单元格然后将其删除时,它会崩溃并显示以下错误消息,而不是删除并显示消息。***终止应用程序由于未捕获的异常 'NSInternalInconsistencyException'而退出,原因是:“UITableView内部错误:无法使用旧部分计数生成新的部分映射:1和新的部分计数:0”。我不知道为什么返回相同的值会起作用,但是它确实可以按照预期运行。 - ChrisOSX
4
请不要使用这个解决方案。表格视图的数据源方法不能超出它们应该做的事情范围。numberOfSections 应当返回一个计数,仅此而已。numberOfRowsInSection 同样如此。这些方法可以在任何时候被多次调用。永远不要更新视图或更新数据或者进行任何除了返回计数以外的操作。更新视图的逻辑绝不能在这些方法中实现。 - rmaddy
显示剩余12条评论

111

针对 Xcode 8.3.2 - Swift 3.1

这里介绍一种不太为人知但非常简单的方法,用于向一个空表格视图中添加“无条目”视图。这个方法从 Xcode 7 开始就可以使用了。我会让你自己控制将该视图添加/移除到表格的背景视图上的逻辑,但下面是 Xcode(8.3.2)故事板的步骤:

  1. 在故事板中选择包含您的表格视图的场景。
  2. 将一个空的 UIView 拖到场景的 "Scene Dock" 中。

enter image description here

  1. 在新视图上添加一个 UILabel 和任何约束,然后为该视图创建一个 IBOutlet。

enter image description here

  1. 将该视图分配给 tableView.backgroundView。

enter image description here

  1. 看魔术吧!

enter image description here

最终,无论何时您想要向您的视图控制器添加一个简单的视图,并且您不一定希望立即显示它,但又不想手动编写代码时,这个方法都是有效的。


1
这种技术适用于需要简单视图的任何内容。我还将其用于自定义表格部分标题。实际上,您可以将其用于您需要视图的任何内容。只需通过IBOutlet引用并将其添加为子视图即可在需要自定义视图的任何位置使用它。 - Paul Bonneville
正如@gmogames所说,做了2年的IOS开发,这是我第一次知道tableView背景!谢谢你。 - Mohammed Riyadh
1
谢谢你。只是想说一下,我不得不使用tableView.separatorStyle = .none并调整默认的UIView大小才能看到backgroundView。我正在使用带有Swift 4的Xcode 9.4。 - Hammad Tariq
你如何放置自定义视图? - Nol4635
1
@Nol4635,在表视图背景方面,你只需要将你创建的视图分配为IBOutlet到表视图的背景中或者,如果你只想将视图添加到任何其他视图中,则可以将其作为子视图添加。你必须设置框架值,但它像任何其他视图一样。 - Paul Bonneville

30

以上代码的 Swift 版本:

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。 - Chathuranga Silva
如果 jobs_list.count == 0 { // 添加上述代码 } else { self.tableView.backgroundView = nil } - Chathuranga Silva
在我的if语句中添加self.tableView.backgroundView = nil,然后再返回jobs_list.count就可以了。谢谢。 - tylerSF
不要使用此答案中的第一个解决方案。表视图数据源方法绝不能超出其预期功能。numberOfSections 应该返回计数,仅此而已。numberOfRowsInSection 也是如此。这些方法可以在任何时候被多次调用。除了返回计数之外,永远不要更新视图或更新数据或执行任何操作。更新视图的逻辑绝不能在这些方法中。 - rmaddy

7

你可以尝试这个控件,它非常不错。 DZNEmptyDataSet

或者如果我是你,我会这样做:

  • 检查你的数据数组是否为空
  • 如果为空,则向其中添加一个名为@"No Data"的对象
  • 在cell.textLabel.text中显示该字符串

简单易懂


6

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
我同意。这个解决方案可以改进,但也可以使用。 - Teodor Ciuraru
你怎么可以认为不应该使用这个解决方案,却又声称它也可以被使用?那是矛盾的。 - rmaddy
我同意这个解决方案可以改进,但是在当前状态下也能够工作。 - Teodor Ciuraru

6

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
我认为解决你的问题最优雅的方法是从UITableViewController转换为包含UITableViewUIViewController。这样,您可以将任何想要的UIView添加为主视图的子视图。
我不建议使用UITableViewCell来完成此操作,因为您可能需要在未来添加其他内容,而且情况可能会很快变得混乱。
您也可以像这样做,但这也不是最佳解决方案。
UIWindow* window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: OverlayView];

感谢提供这种最优雅的实现方式,这总是一个很好的思考方式。我可能会尝试更改我的故事板,使用包含UITableView的UIViewController。 - SRMR
1
这是最具未来性的方式。我自己从未使用过 UITableViewController,因为使用它并不比用其他方式更容易,而且它会限制您在未来更改事物的能力。 - Cihan Tek
如果您有半透明的栏,则这不是具有未来性的。我强烈反对。至少要创建一个包含UITableViewController的UIViewController,并相应地调整内容。 - 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];
}

1
不要使用这个解决方案。表视图数据源方法绝不能超出其预定功能。numberOfSections 应该返回一个计数,仅此而已。numberOfRowsInSection 也是如此。这些方法可以在任何时候被多次调用。永远不要更新视图或更新数据或执行除返回计数之外的任何操作。更新视图的逻辑绝不能在这些方法中。 - rmaddy

2

如果您不使用tableview footer,也不希望tableview用空的默认table cell填充屏幕,我建议您将tableview footer设置为一个空的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();
    }
}

以上代码将生成一个没有空单元格的表视图。

2
如果tableview没有结果,我会提供一个覆盖视图,具有您想要的外观和消息。您可以在ViewDidAppear中完成此操作,以便在显示/不显示视图之前获取结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接