Adding Space Between the Cells of a UITableView
Adding Space Between the Cells of a UITableView
Photo by Tim Hüfner on Unsplash
Exordium
Thank you for stopping by the Rusty Nail Software dev blog! In this post, I will be showing you how to create a UITableView
that has spacing between the cells. I came across this design as I was recently working on a client’s application. I wanted to get away from the basic table view look and use something tailored more toward the UI/UX of the client’s app. For those who work as freelance software developers — versatility in design is essential because not every client wants the same thing.
To accomplish a clean table view design with spacing between the cells; you must work with sections rather than rows. Let’s get into how to create a custom UITableView
with spacing and clean design. This post assumes that you have previous experience with Swift, Xcode, and UITableViews
.
You can find the source code to this post on GitHub. Be sure to start with the ‘start’ branch of the repo.
What Is Already Set Up
If you open the project and run it on a device or simulator, you will see the current UI layout. I have set up a basic restaurant review application, all done programmatically, and we’ll be working with the fictional burger shack of Rusty Nail. The first view controller you see is the RestaurantReviewsViewController
class. There are two classes in the View
group; one for a custom UITableViewCell
and one for a custom UIView
. In the current state of the app, you can see the UIView
in action, as this is the orange circle that holds the restaurant logo.
Back to the RestaurantReviewsViewController
. First, I’ve stored a custom struct called Review
that holds three properties; the writer of the review, the restaurant receiving the review, and the review’s text. There is also a method named setupView
that sets up the UI elements of the view, and a method named applyAutoConstraints
which activates the constraints for said UI elements.
Furthermore, in the viewDidLoad
method, the view’s background color is set, and the setupView
method gets called. Lastly, you will see a section marked Extensions
and a method that makes a UIView
circled.
Now that we’ve gone through the current code of the main view controller let’s build that table view. If you want to take a look at the other code provided in the project, take your time and get to know how it all works together. Briefly, the RestaurantReviewCell
is a custom table view cell that contains two labels; one for the name of the person reviewing the restaurant and one for the review.
Configuring the Table View
To start, initialize the data
array with some reviews. In this case, I’ve used characters from the show Bob’s Burgers. The data is initialized in the setupView
method.
data = [
Review(writer: “Calvin”, receiver: “Rusty Nail Burgers”, reviewText: “Okay burgers. Okay tenant. Would like to see them pay rent on time.”),
Review(writer: “Felix”, receiver: “Rusty Nail Burgers”, reviewText: “Though my brother isn’t a fan of Bob’s cooking, I can’t find a better burger on the wharf.”),
Review(writer: “Teddy”, receiver: “Rusty Nail Burgers”, reviewText: “I’m here everyday. Couldn’t ask for a better hangout spot.”),
Review(writer: “Mickey”, receiver: “Rusty Nail Burgers”, reviewText: “Hey Bob! Thanks for feeding me during that heist. I will definitely be back to visit.”),
Review(writer: “Marshmellow”, receiver: “Rusty Nail Burgers”, reviewText: “Hey Bob.”),
Review(writer: “Rudy”, receiver: “Rusty Nail Burgers”, reviewText: “My dad drops me off here every once in a while. I like to sit in the back corner and enjoy my food.”),
]
Next, set the delegate
and dataSource
of the reviewTableView
. I will conform to UITableViewDelegate
and UITableViewDataSource
protocols in the section marked Extensions
— though you can do this directly off the RestaurantReviewsViewController
class. Add the following to the setupView
method after reviewTableView
is initialized:
reviewTableView.delegate = self
reviewTableView.dataSource = self
The setupview
method should now look like this:
func setupView() {
data = [
Review(writer: “Calvin”, receiver: “Rusty Nail Burgers”, reviewText: “Okay burgers. Okay tenant. Would like to see them pay rent on time.”),
Review(writer: “Felix”, receiver: “Rusty Nail Burgers”, reviewText: “Though my brother isn’t a fan of Bob’s cooking, I can’t find a better burger on the wharf.”),
Review(writer: “Teddy”, receiver: “Rusty Nail Burgers”, reviewText: “I’m here everyday. Couldn’t ask for a better hangout spot.”),
Review(writer: “Mickey”, receiver: “Rusty Nail Burgers”, reviewText: “Hey Bob! Thanks for feeding me during that heist. I will definitely be back to visit.”),
Review(writer: “Marshmellow”, receiver: “Rusty Nail Burgers”, reviewText: “Hey Bob.”),
Review(writer: “Rudy”, receiver: “Rusty Nail Burgers”, reviewText: “My dad drops me off here every once in a while. I like to sit in the back corner and enjoy my food.”),
]
restaurantImageViewBackground = RestaurantImageViewBackground()
view.addSubview(restaurantImageViewBackground)
restaurantImageView = UIImageView(image: UIImage(named: “cheese-burger”))
restaurantImageView.translatesAutoresizingMaskIntoConstraints = false
restaurantImageViewBackground.addSubview(restaurantImageView)
restaurantNameLabel = UILabel()
restaurantNameLabel.translatesAutoresizingMaskIntoConstraints = false
restaurantNameLabel.text = “Rusty Nail Burgers”
restaurantNameLabel.font = UIFont.boldSystemFont(ofSize: 28)
restaurantNameLabel.textColor = .black
view.addSubview(restaurantNameLabel)
starStackView = UIStackView()
starStackView.translatesAutoresizingMaskIntoConstraints = false
starStackView.alignment = .center
starStackView.axis = .horizontal
starStackView.spacing = 5
for _ in 0…5 {
let starView = UIImageView(image: UIImage(systemName: “star.fill”))
starView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
starView.tintColor = .black
starStackView.addArrangedSubview(starView)
}
view.addSubview(starStackView)
reviewTableView = UITableView()
reviewTableView.translatesAutoresizingMaskIntoConstraints = false
reviewTableView.delegate = self
reviewTableView.dataSource = self
view.addSubview(reviewTableView)
applyAutoConstraints()
}
The UITableViewDataSource
is set up like any other table view. We return one row in each section and configure the cells based on our custom RestaurantReviewCell
class. Before setting up the cell, register the UITableViewCell
class used with the table view. Add the following to UITableView
setup:
reviewTableView.register(RestaurantReviewCell.self, forCellReuseIdentifier: “reviewCell”)
To set up the cell, add the following to the cellForRowAt
method:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// 1
let cell = tableView.dequeueReusableCell(withIdentifier: “reviewCell”, for: indexPath) as! RestaurantReviewCell
// 2
if let writer = data[indexPath.section].writer {
cell.reviewerLabel.text = “\(writer) said:”
}
if let reviewText = data[indexPath.section].reviewText {
cell.reviewLabel.text = “\(reviewText)”
}
// 3
return cell
}
Here’s what that does:
1. Dequeue the cell, using the reviewCell
identifier and the RestaurantReviewCell
class.
2. Pull the data, based on the section being displayed. First, unwrap the value of the writer
property of our data source (remember that this is of the type Review
). Then, unwrap the value of reviewText
.
3. Return the cell.
UI Work with the Table View
Next, in the UITableViewDelegate
, set the number of sections of the table view to the count
of the array data
to create the number of sections needed to display the correct amount of data.
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
Set the height of the rows to 140 (this can be changed according to your use case).
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
Now, to add the spacing to the cells, we must create a view for each section’s header. Call the viewForHeaderInSection
method, and add the following:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// 1
let headerView = UIView()
// 2
headerView.backgroundColor = view.backgroundColor
// 3
return headerView
}
Here is the breakdown of what was just added:
1. Create the header view as a UIView
.
2. Set the background color of the header to the view controller’s view.backgroundColor
property. (This creates a design effect that makes the header look transparent).
3. Return the headerView
to set the header for each section of the table view.
This produces a basic spacing between the cells. The height of the header can be set as followed:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 20
}
The UITableViewDelegate
and UITableViewDataSource
should look like this:
extension RestaurantReviewsViewController: UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = view.backgroundColor
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 20
}
}
extension RestaurantReviewsViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: “reviewCell”, for: indexPath) as! RestaurantReviewCell
if let writer = data[indexPath.section].writer {
cell.reviewerLabel.text = “\(writer) said:”
}
if let reviewText = data[indexPath.section].reviewText {
cell.reviewLabel.text = “\(reviewText)”
}
return cell
}
}
Finishing Up
Earlier I mentioned a ‘clean’ design. Right now, each cell’s design looks like a basic UITableViewCell
; a white rectangle. To add to the ‘cleanliness’ of the design, change the leadingAnchor
and trailingAnchor
to push the table view sides 20 points away from each side of the view:
reviewTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
reviewTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
The cells are still basic white rectangles, even though the RestaurantReviewCell
has its layer.cornerRadius
set to 20. This is because the table view’s background color is white. To fix this, we must remove the background color of the table view, which is currently white. Remove the background of the table view by setting the backgroundColor
property of the table view to .clear
.
reviewTableView.backgroundColor = .clear
Finally, since this is a basic list, tapping the cells is useless and doesn’t add to the user experience. To disable tapping on the table view, set the allowsSelection
property to false
.
reviewTableView.allowsSelection = false
Closing Notes
Thanks for stopping by Rusty Nail Software and reading one of our blog posts! If you’d like to get in touch with me, you can find me on Twitter and LinkedIn. If you and your team are looking for a dependable iOS Developer, let’s talk about your project. Feel free to get in touch on social media, or via email. You can also read more of my writings on my blog.