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!
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")
}
}