Handling an empty UITableView. Print a friendly message

前端 未结 22 1204
青春惊慌失措
青春惊慌失措 2020-12-04 05:22

I have a UITableView that in some cases it is legal to be empty. So instead of showing the background image of the app, I would prefer to print a friendly message in the scr

22条回答
  •  青春惊慌失措
    2020-12-04 06:03

    There is a specific use case for multiple data sets and sections, where you need an empty state for each section.

    You can use suggestions mentioned in multiple answers to this question - provide custom empty state cell.

    I'll try to walk you through all the steps programmatically in more detail and hopefully, this will be helpful. Here's the result we can expect:

    For simplicity's sake, we will work with 2 data sets (2 sections), those will be static.

    I will also assume that you have the rest of your tableView logic working properly with datasets, tabvleView cells, and sections.

    Swift 5, let's do it:

    1. Create a custom empty state UITableViewCell class:

    class EmptyTableViewCell: UITableViewCell {
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            setupView()
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        let label: UILabel = {
            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false
            label.text = "Empty State Message"
            label.font = .systemFont(ofSize: 16)
            label.textColor = .gray
            label.textAlignment = .left
            label.numberOfLines = 1
            return label
        }()
    
        private func setupView(){
            contentView.addSubviews(label)
            let layoutGuide = contentView.layoutMarginsGuide
            NSLayoutConstraint.activate([
                label.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor),
                label.topAnchor.constraint(equalTo: layoutGuide.topAnchor),
                label.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor),
                label.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor),
                label.heightAnchor.constraint(equalToConstant: 50)
            ])
        }
    }
    

    2. Add the following to your UITableViewController class to register your empty cell:

    class TableViewController: UITableViewController {
        ...
        let emptyCellReuseIdentifier = "emptyCellReuseIdentifier"
        ...
        override func viewDidLoad(){
            ...
            tableView.register(EmptyTableViewCell.self, forCellReuseIdentifier: emptyCellReuseIdentifier)
            ...
        }
    }
    

    3. Now let's highlight some assumptions mentioned above:

    class TableViewController: UITableViewController {
        // 2 Data Sets
        var firstDataSet: [String] = []
        var secondDataSet: [String] = []
    
        // Sections array
        let sections: [SectionHeader] = [
            .init(id: 0, title: "First Section"),
            .init(id: 1, title: "Second Section")
        ]
        ...
        // MARK: - Table view data source
        override func numberOfSections(in tableView: UITableView) -> Int {
            sections.count
        }
        override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
            return sections[section].title
        }
            ...
        }
    
    struct SectionHeader {
        let id: Int
        let title: String
    }
    

    4. Now let's add some custom logic to our Data Source to handle Empty Rows in our sections. Here we are returning 1 row if a data set is empty:

    class TableViewController: UITableViewController {
        ...
        // MARK: - Table view data source
        ...
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            switch section{
            case 0:
                let numberOfRows = firstDataSet.isEmpty ? 1 : firstDataSet.count
                return numberOfRows          
            case 1:
                let numberOfRows = secondDataSet.isEmpty ? 1 : secondDataSet.count
                return numberOfRows   
            default:
                return 0
            }
        }
        ...
    }
    

    5. Lastly, the most important "cellForRowAt indexPath":

    class TableViewController: UITableViewController {
        ...
        // MARK: - Table view data source
        ...
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            // Handle Empty Rows State
            switch indexPath.section {
            case 0:
                if firstDataSet.isEmpty {
                    if let cell = tableView.dequeueReusableCell(withIdentifier: emptyCellReuseIdentifier) as? EmptyTableViewCell {
                        cell.label.text = "First Data Set Is Empty"
                        return cell
                    }
                }
            case 1:
                if secondDataSet.isEmpty {
                    if let cell = tableView.dequeueReusableCell(withIdentifier: emptyCellReuseIdentifier) as? EmptyTableViewCell {
                        cell.label.text = "Second Data Set Is Empty"
                        return cell
                    }
                }
            default:
                break
            }
            
            // Handle Existing Data Sets
            if let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as? TableViewCell {
                switch indexPath.section {
                case 0:
                    ...
                case 1:
                    ...
                default:
                    break
                }
                return cell
            }
    
            return UITableViewCell()
        }
        ...
    }
    

提交回复
热议问题