noblerare
noblerare

Reputation: 11873

Swift - Customize view to show over an empty table cells

I am using Swift 3, Xcode 8.2.

I've been able to create a label to cover the empty table view cells when there are none to display.

My code is below and it is located in the subclass of UITableViewController.

override func numberOfSections(in tableView: UITableView) -> Int {

    // if there are scans to display...
    if items.count > 0 {
        tableView.backgroundView = nil
        tableView.separatorStyle = .singleLine
        return 1
    }
    else { // otherwise, return 0, remove cell lines, and display a Label
        let rect = CGRect(x: 0,
                          y: 0,
                          width: tableView.bounds.size.width,
                          height: tableView.bounds.size.height)
        let noScanLabel: UILabel = UILabel(frame: rect)

        noScanLabel.text = "No Scans"
        noScanLabel.textColor = UIColor.gray
        noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)

        noScanLabel.textAlignment = NSTextAlignment.center



        tableView.backgroundView = noScanLabel
        tableView.separatorStyle = .none



        return 0
    }
}

Here is the result.

enter image description here

Looks fine. But how, do I make it such that I include another line of text with a downward arrow pointing at the raised center button. Something like "Click here to start a scan"?

I've tried adding new line characters to the noScanLabel.text field but that didn't work out. Any pointers in the right direction would be helpful.

Upvotes: 0

Views: 1238

Answers (3)

DJ_Mobi90
DJ_Mobi90

Reputation: 139

You can take UIView and add your all UILabel and arrow Image on UIView and then assign that UIView to backgroundView of TableView.

Like this.

override func numberOfSections(in tableView: UITableView) -> Int {

    // if there are scans to display...
    if items.count > 0 {
        tableView.backgroundView = nil
        tableView.separatorStyle = .singleLine
        return 1
    }
    else { // otherwise, return 0, remove cell lines, and display a Label
        let rect = CGRect(x: 0,
                          y: 0,
                          width: tableView.bounds.size.width,
                          height: tableView.bounds.size.height)
        let messageBaseView = UIView(frame: rect)

        //Add your first label..
        let noScanLabel: UILabel = UILabel()
        noScanLabel.text = "No Scans"
        noScanLabel.textColor = UIColor.gray
        noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
        noScanLabel.textAlignment = NSTextAlignment.center
        messageBaseView.addSubView(noScanLabel)

        //Add your second label.. and your arrow image here on messageBaseView

        //Assign messageBaseView to backgroundView of tableView
        tableView.backgroundView = messageBaseView
        tableView.separatorStyle = .none



        return 0
    }
}

Upvotes: 0

Abishek Gokal
Abishek Gokal

Reputation: 186

There are a few ways achieve your goal. There is a well known library called DZNEmptyDataSet for handling empty tableviews and collectionviews . https://github.com/dzenbot/DZNEmptyDataSet

The other way would be to create a uiview with your specified rect and then add two labels to that uiview. One would be your noScanLabel and the other would be a label or image containing your arrow. You can set the layout constraints as required so that the arrow appears pointing down.

This code seems to work well. Change constraints if needed

   let rect = CGRect(x: 0, y: 0, width: tableview.bounds.size.width, height: tableview.bounds.size.height)
    let noDataView = UIView(frame: rect)
    let noScanLabel = UILabel()
    noScanLabel.text = "No Scans"
    noScanLabel.textColor = UIColor.gray
    noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
    noScanLabel.textAlignment = NSTextAlignment.center
    let arrowLabel = UILabel()
    arrowLabel.text = "Add Arrow Image to this label"
    arrowLabel.textColor = UIColor.gray
    arrowLabel.font = UIFont.boldSystemFont(ofSize: 24)
    arrowLabel.textAlignment = NSTextAlignment.center

    noScanLabel.widthAnchor.constraint(equalToConstant: 100)
    arrowLabel.widthAnchor.constraint(equalToConstant: 50)
    noDataView.addSubview(noScanLabel)
    noDataView.addSubview(arrowLabel)

    arrowLabel.translatesAutoresizingMaskIntoConstraints = false
    noDataView.translatesAutoresizingMaskIntoConstraints = false
    noScanLabel.translatesAutoresizingMaskIntoConstraints = false


    self.tableview.addSubview(noDataView)
    noDataView.isHidden = false
    noDataView.centerXAnchor.constraint(equalTo: self.tableview.centerXAnchor).isActive = true
    noDataView.centerYAnchor.constraint(equalTo: self.tableview.centerYAnchor).isActive = true

    noScanLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
    noScanLabel.topAnchor.constraint(equalTo: noDataView.centerYAnchor).isActive = true

    arrowLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
    arrowLabel.topAnchor.constraint(equalTo: noScanLabel.bottomAnchor).isActive = true

The other option is to set number of lines to zero as mentioned already

noScanLabel.numberLines = 0 

Upvotes: 1

tomahh
tomahh

Reputation: 13661

The simple solution is to set numberOfLines to 0 on noScanLabel. This way, the new lines will show.

let noScanLabel: UILabel = UILabel(frame: rect)

noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.numberOfLines = 0

noScanLabel.textAlignment = NSTextAlignment.center

Note than in such cases, I would recommend, for better maintainability, to actually remove the TableView from the UIViewController (hence not inherit from UITableViewController) and replace it with an empty view when you detect no scans are available. This will make each state more independent of each other and make maintenance easier.

Upvotes: 4

Related Questions