UITableView & UITableViewCell Without Storyboard — Swift 5

Daniel Kioko
TheCodr
Published in
4 min readMay 20, 2021

Let’s code a simple feed in Swift without using Storyboards or Nib files.

There’s a number of reasons why using storyboards won’t be the best option for you. So why not improve your development process by defining all the UI components you need in Swift? We’ll do just that in this tutorial.

Start by creating a new Xcode project in UIKit and open the ViewController.swift file to begin.

UITableView

We’ll start by defining a table — I started by registering the Table View Cell, setting the row height, and setting translatesAutoresizingMaskIntoConstraints to false since I was about to define constraints — We’ll be doing the same for all UI Components we’ll define.

let tableView: UITableView = {
let table = UITableView()
table.translatesAutoresizingMaskIntoConstraints = false
table.register(TableCell.self, forCellReuseIdentifier: "cell")
table.rowHeight = 470
table.layer.backgroundColor = UIColor.white.cgColor
table.tableFooterView = UIView(frame: .zero)
return table
}()

Next, we’ll setup the constraints. I set my table to stick to all 4 anchors of the view controller without leaving any space. Don’t forget to activate them!

fileprivate func setupConstraints() {
view.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
}

UITableViewCell

I wanted to create a simple but nice looking card holding an image, a profile photo, and a username.

We’ll ahead to create an empty swift file and create a class for UITableViewCell. (You can also create one using CocoaTouch option without a Nib file)

I started by defining the first layer which is a UIView

let cardView: UIView = {
let view = UIView()
view.layer.cornerRadius = 14
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
return
view
}()

Then the UIImageViews for the post image and the profile photo.

let postImageView: UIImageView = {
let imageView = UIImageView()
imageView.layer.cornerRadius = 14
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
return
imageView
}()
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.layer.cornerRadius = 25
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return
imageView
}()

And finally, a UILabel for the username.

let usernameLabel: UILabel = {
let label = UILabel()
label.text = "@itsdanielkioko"
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 14)
label.translatesAutoresizingMaskIntoConstraints = false
return
label
}()

If you created a class for UITableViewCell with CocoaTouch, you might have the ‘override func awakeFromNib’ function by default. Since we’re programmatically defining our cell, we’ll delete the function.

We’ll then replace it with an ‘init’ function for the cell.

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: "cell")
}

You’ll also need to have to create a ‘requiredInit’ function since you’ll be creating a sub-class. An error might pop-up on Xcode to give you the option to create one.

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

Inside the ‘override init’ function, we’ll add the interface components we defined and add constraints to them.

//1st LAYER - UIView
addSubview(cardView)
cardView.topAnchor.constraint(equalTo: topAnchor, constant: 12).isActive = true
cardView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 12).isActive = true
cardView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12).isActive = true
cardView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12).isActive = true
//2nd LAYER - UIImageView
cardView.addSubview(postImageView)
postImageView.topAnchor.constraint(equalTo: cardView.topAnchor, constant: 12).isActive = true
postImageView.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -12).isActive = true
postImageView.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 12).isActive = true
postImageView.trailingAnchor.constraint(equalTo: cardView.trailingAnchor, constant: -12).isActive = true
//3rd LAYER - UIImageView
cardView.addSubview(profileImageView)
profileImageView.heightAnchor.constraint(equalToConstant: 50).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 50).isActive = true
profileImageView.topAnchor.constraint(equalTo: postImageView.topAnchor, constant: 12).isActive = true
profileImageView.leadingAnchor.constraint(equalTo: postImageView.leadingAnchor, constant: 12).isActive = true
//4th LAYER - UILabel
cardView.addSubview(usernameLabel)
usernameLabel.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor).isActive = true
usernameLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 8).isActive = true
//Let's remove selection style from the cell as well
selectionStyle = .none

Populating UITableView Rows

Finally, let’s fill the cells with some pictures to make it lively — I used some free images from Unsplash. I copied the url of the photos into an array.

let images = ["url1", "url2", "url3"]

I then created an extension to UIImageView to easily load images from Urls so I could use it inside the ‘cellForRowAt’ function

extension UIImageView {
func load(url: URL) {
DispatchQueue.global().async { [weak self] in
if
let data = try? Data(contentsOf: url) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.image = image
}
}
}
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableCell
cell.postImageView.load(url: URL(string: photos[indexPath.row])!)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return photos.count
}
}

That’s it! See the GitHub repository for all the code if you need further reference.

Thanks for reading and Happy Coding! 🌟

Results

--

--