Label on Row Separator - Swift Tableview - Hourly

2019-08-03 06:13发布

问题:

I want to create an hourly calendar view that is relatively basic, but similar to Apple's native calendar view. How do you add labels to be in line with the row/cell separators, and not contained in a cell. Like this:

Is there a property that lets you add a label to the lines? Do the labels have to be placed outside of the table view? Or is there a separate table that occurs?

In terms of creating colored blocks to represent events on the calendar, what would be the best way to go about doing this? Would it just be a CGRect in a prototype cell? Would you need to create separate xib files?

Thanks in advance for the help, I am still new to learning Swift!

回答1:

It's not possible (or technically, it would be possible, but the overhead is too high, considering your other options).

Instead of using cell separators, set separatorStyle = .none, and draw the line in the cell (e.g., as a UIView with view.height = 1 and view.backgroundColor = .grey) and normally add the label in the cell.

Basically the solution is very simple: disable standard separator lines, and rather draw separator inside the cell (bottom or top) along with the labels. That's how I've been doing things when the client asked for some custom fancy separators - I added a custom line at the bottom of the cell and used the rest of the cell's contentView as for the cell's content.

EDIT

You can use a following example to start with (note that this is just one of several different approaches how to manage it):

class TimeCellViewController: UITableViewController {
    override func loadView() {
        super.loadView()

        // you can use UITableViewAutomaticDimension instead of static height, if
        // there will be variable heights that you don't know upfront
        // https://stackoverflow.com/a/18746930/2912282
        // or mine:
        // https://stackoverflow.com/a/47963680/2912282
        tableView.rowHeight = 80
        tableView.estimatedRowHeight = 80

        tableView.separatorStyle = .none

        // to allow scrolling below the last cell
        tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 40))

        tableView.register(TimeCell.self, forCellReuseIdentifier: "timeCell")
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 24
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "timeCell", for: indexPath) as! TimeCell
        if indexPath.row > 0 {
            cell.topTime = "\(indexPath.row):00"
        } else {
            cell.topTime = ""
        }
        cell.bottomTime = "\(indexPath.row + 1):00"
        return cell
    }
}

class TimeCell: UITableViewCell {
    // little "hack" using two labels to render time both above and below the cell
    private let topTimeLabel = UILabel()
    private let bottomTimeLabel = UILabel()

    private let separatorLine = UIView()

    var topTime: String = "" {
        didSet {
            topTimeLabel.text = topTime
        }
    }
    var bottomTime: String = "" {
        didSet {
            bottomTimeLabel.text = bottomTime
        }
    }

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        selectionStyle = .none

        contentView.addSubview(topTimeLabel)
        contentView.addSubview(bottomTimeLabel)
        contentView.addSubview(separatorLine)


        topTimeLabel.textColor = UIColor.gray
        topTimeLabel.textAlignment = .right
        bottomTimeLabel.textColor = UIColor.gray
        bottomTimeLabel.textAlignment = .right
        separatorLine.backgroundColor = UIColor.gray

        bottomTimeLabel.translatesAutoresizingMaskIntoConstraints = false
        topTimeLabel.translatesAutoresizingMaskIntoConstraints = false
        separatorLine.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            bottomTimeLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0),
            bottomTimeLabel.centerYAnchor.constraint(equalTo: self.bottomAnchor),
            bottomTimeLabel.widthAnchor.constraint(equalToConstant: 50),
            topTimeLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0),
            topTimeLabel.centerYAnchor.constraint(equalTo: self.topAnchor),
            topTimeLabel.widthAnchor.constraint(equalToConstant: 50),
            separatorLine.leftAnchor.constraint(equalTo: bottomTimeLabel.rightAnchor, constant: 8),
            separatorLine.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            separatorLine.heightAnchor.constraint(equalToConstant: 1),
            separatorLine.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0),
            ])

        // if you use UITableViewAutomaticDimension instead of static height,
        // you will have to set priority of one of the height constraints to 999, see
        // https://stackoverflow.com/q/44651241/2912282
        // and
        // https://stackoverflow.com/a/48131525/2912282
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}