Building TableViewCell with SwiftUI

Mesut Aygun
3 min readDec 11, 2023

--

UIKit & SwiftUI

In the dynamic world of iOS development, harnessing the power of both SwiftUI and UIKit opens up a realm of possibilities. In this tutorial, we’ll delve into the seamless integration of UIKit’s UITableView in SwiftUI, exploring step-by-step how to create a robust and interactive TableView within your SwiftUI project.

If we examine it through an example…

import Foundation

struct TableViewModel: Codable {
let id: Int
let title: String
let url: String
}

Let’s fetch the sample data using this view model.

class FirstTableViewModel {

var tableview_Data: [TableView_Model] = []

func fetchDataForTableView(completion: @escaping ([TableView_Model]?) -> Void) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/albums/1/photos") else {
completion(nil)
return
}

URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("Error: \(error?.localizedDescription ?? "Unknown error")")
completion(nil)
return
}

do {
let decoder = JSONDecoder()
let photos = try decoder.decode([TableView_Model].self, from: data)
self.tableview_Data = photos
completion(photos)

} catch {
print("Error decoding data: \(error.localizedDescription)")
completion(nil)
}
}.resume()


}
}

Let’s utilize the conventional approach to display the fetched data in the TableView.

extension FirstViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return photoViewModel.numberOfPhotos()
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PhotoCell", for: indexPath) as! PhotosTableViewCell
let data = photoViewModel.photo(at: indexPath.row)
cell.configure(with: data)
return cell
}
}

In the cellForRowAt function, we used the PhotosTableViewCell layout.

class PhotosTableViewCell: UITableViewCell {

let photoImageView : UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 13
return imageView
}()

let titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 3
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()

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

required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}

private func setupViews() {
contentView.addSubview(photoImageView)
contentView.addSubview(titleLabel)

NSLayoutConstraint.activate([
photoImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
photoImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
photoImageView.widthAnchor.constraint(equalToConstant: 80),
photoImageView.heightAnchor.constraint(equalToConstant: 80),

titleLabel.leadingAnchor.constraint(equalTo: photoImageView.trailingAnchor, constant: 8),
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)

])
}

Let’s replace the PhotosTableViewCell with the SwiftUI-generated view and explore how we can effortlessly create more modern, simpler pages in a much easier and more practical manner.

struct TableViewCell_SwiftUI : View {
let data : TableView_Model
var body : some View {

HStack{
AsyncImage(url: URL(string: data.url)) { image in
image.resizable()
.frame(width: 80, height: 80)
.scaledToFit()
.cornerRadius(12)
} placeholder: {
Image(systemName: "person")
}


Text(data.title)
Spacer()
Image(systemName: "chevron.right")
}
}
}

After creating the view in SwiftUI, all we need to do is define it in the cellForRow function.

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PhotoCell", for: indexPath)
let data = photoViewModel.photo(at: indexPath.row)
// cell.configure(with: data)

// Instead of using a configure method, set the contentConfiguration of the cell using UIHostingConfiguration

cell.contentConfiguration = UIHostingConfiguration(content: {
TableViewCell_SwiftUI(data: data)
})
return cell
}
  • UIHostingConfiguration is a class provided by SwiftUI to facilitate the integration of SwiftUI views into UIKit components.
  • Finally, you assign the UIHostingConfiguration instance, which encapsulates your SwiftUI view, to the contentConfiguration property of the UIKit cell. This essentially tells the UIKit cell to use your SwiftUI view as its content.

Exploring the synergy between SwiftUI and UIKit in creating dynamic and visually appealing TableView layouts has been both enlightening and empowering. As we conclude our journey, may your SwiftUI-powered UITableViews bring forth not only seamless functionality but also a delightful user experience. Happy coding!

--

--