Can not add spacing between UITableViewCells

75 Views Asked by At

I am currently working on a project where I will be using an API to receive some suggestions, which I need to display as a tableview.

My goal is to add a 20-point margin between the cells in the tableview to make the suggestions more visually distinct and easier to read.

However, the methods I have tried so far to add this margin have not been successful.

I am looking for alternative solutions or suggestions on how to achieve the desired margin between the cells in the tableview.

import UIKit
import SnapKit

class SecondOnboardingPageVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
    var selectedIndexPath: IndexPath?

    let tableData = ["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eicia deserunt mollit anim id"]
    
    
    private let tableView: UITableView = {
        let tableView = UITableView()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.backgroundColor = .bgColor
        tableView.rowHeight = UITableView.automaticDimension
        
        return tableView
    }()
    
    private let selectLabel: UILabel = {
        let label = UILabel()
        label.text = "Select An Idea"
        label.font = UIFont(name: "Avenir", size: 30)
        return label
    }()
    
    private let continueButton: UIButton = {
        let button = UIButton()
        button.setTitle("Continue", for: .normal)
        button.backgroundColor = .black
        button.layer.cornerRadius = 10
        button.addTarget(self, action: #selector(nextVC), for: .touchUpInside)
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.separatorStyle = .none
        
        
        tableView.separatorInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)

        self.navigationController?.isNavigationBarHidden = true

    }
    
    private func setupUI() {
        view.backgroundColor = .bgColor
        view.addSubview(selectLabel)
        view.addSubview(continueButton)
        view.addSubview(tableView)
        
    
        selectLabel.snp.makeConstraints { make in
            make.top.equalTo(view.safeAreaLayoutGuide).offset(0)
            make.centerX.equalToSuperview()
        }
        continueButton.snp.makeConstraints { make in
            make.bottom.equalToSuperview().inset(30)
            make.left.equalToSuperview().offset(40)
            make.right.equalToSuperview().inset(40)
            make.height.equalTo(56)
        }
        tableView.snp.makeConstraints { make in
            make.top.equalTo(selectLabel.snp.bottom).offset(20)
            make.left.equalToSuperview().offset(20)
            make.right.equalToSuperview().inset(20)
            make.bottom.equalTo(-160)
        }
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 7
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = tableData.first
        cell.textLabel?.numberOfLines = 0
        cell.textLabel?.lineBreakMode = .byWordWrapping
        cell.backgroundColor = .viewColor
        cell.layer.cornerRadius = 10
        
        cell.contentView.layoutMargins = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
        cell.contentView.preservesSuperviewLayoutMargins = false
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 30
        
    }
    
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let cell = tableView.cellForRow(at: indexPath) {
            for visibleCell in tableView.visibleCells {
                visibleCell.backgroundView = nil
            }
            let newView = UIView()
            newView.backgroundColor = .orangeColor
            cell.backgroundView = newView
            selectedIndexPath = indexPath
            tableView.deselectRow(at: indexPath, animated: true)
        }
    }
    @objc private func nextVC()  {
        let controller = FifthOnboardingPageVC()
        navigationController?.pushViewController(controller, animated: true)
        
    }
}

I will be receiving some suggestions from an API, and I need to list these suggestions as a tableview. I want to add a 20 margin between the cells, but the methods I have tried are not working.

I have tried many methods, but none of them worked.

1

There are 1 best solutions below

1
DonMag On BEST ANSWER

First... all of this:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.textLabel?.text = tableData.first
    cell.textLabel?.numberOfLines = 0
    cell.textLabel?.lineBreakMode = .byWordWrapping
    cell.backgroundColor = .viewColor
    cell.layer.cornerRadius = 10
    
    cell.contentView.layoutMargins = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
    cell.contentView.preservesSuperviewLayoutMargins = false
    
    return cell
}

is a bad idea.

What you want to do is create a custom cell, designed like this:

  • "rounded corners" view, as a subview of the cell's .contentView, inset by 10-points on all 4 sides.
  • a multi-line label, as a subview of "rounded corners" view, inset by 10-points on all 4 sides.

The code you posted didn't include your .bgColor or .orangeColor definitions, so I'll use:

extension UIColor {
    // very light gray
    static let bgColor: UIColor = .init(white: 0.95, alpha: 1.0)
    // orange
    static let orangeColor: UIColor = .systemOrange
}

Quick example cell (since you're using SnapKit):

class ExampleCustomCell: UITableViewCell {
    
    let theLabel = UILabel()
    let roundedView = UIView()
    
    let normalColor: UIColor = .bgColor
    let selectedColor: UIColor = .orangeColor
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
    
        // properties
        theLabel.numberOfLines = 0
        
        roundedView.layer.cornerRadius = 10
        
        roundedView.addSubview(theLabel)
        contentView.addSubview(roundedView)

        theLabel.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(10.0)
        }
        roundedView.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(10.0)
        }
        
    }
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        roundedView.backgroundColor = isSelected ? selectedColor : normalColor
    }

}

if you notice, we're overriding setSelected() so we can have the cell itself handle setting the color of the rounded view.

Now the cellForRowAt function is much simpler:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! ExampleCustomCell
    let str: String = tableData.first ?? ""
    cell.theLabel.text = "\(indexPath.row): \(str)"
    // we don't want the cell to use its default selection style
    cell.selectionStyle = .none
    return cell
    
}

and, because we're letting the table view and cell classes handle the row selection, we don't need to do anything in didSelectRowAt.

Here'a the complete SecondOnboardingPageVC:

class SecondOnboardingPageVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
    var selectedIndexPath: IndexPath?
    
    let tableData = ["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eicia deserunt mollit anim id"]
    
    private let tableView: UITableView = {
        let tableView = UITableView()
        tableView.backgroundColor = .bgColor
        return tableView
    }()
    
    private let selectLabel: UILabel = {
        let label = UILabel()
        label.text = "Select An Idea"
        label.font = UIFont(name: "Avenir", size: 30)
        return label
    }()
    
    private lazy var continueButton: UIButton = {
        let button = UIButton()
        button.setTitle("Continue", for: .normal)
        button.backgroundColor = .black
        button.layer.cornerRadius = 10
        button.addTarget(self, action: #selector(nextVC), for: .touchUpInside)
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupUI()
        
        tableView.dataSource = self
        tableView.delegate = self
        
        tableView.separatorStyle = .none
        
        // register custom cell
        tableView.register(ExampleCustomCell.self, forCellReuseIdentifier: "customCell")
        
        self.navigationController?.isNavigationBarHidden = true
    }
    
    private func setupUI() {
        view.backgroundColor = .systemBackground // .bgColor
        view.addSubview(selectLabel)
        view.addSubview(continueButton)
        view.addSubview(tableView)
        
        
        selectLabel.snp.makeConstraints { make in
            make.top.equalTo(view.safeAreaLayoutGuide).offset(0)
            make.centerX.equalToSuperview()
        }
        continueButton.snp.makeConstraints { make in
            make.bottom.equalToSuperview().inset(30)
            make.left.equalToSuperview().offset(40)
            make.right.equalToSuperview().inset(40)
            make.height.equalTo(56)
        }
        tableView.snp.makeConstraints { make in
            make.top.equalTo(selectLabel.snp.bottom).offset(20)
            make.left.equalToSuperview().offset(20)
            make.right.equalToSuperview().inset(20)
            make.bottom.equalTo(-160)
        }
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // let's return 17 so we can confirm all is working when scrolling
        return 17
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! ExampleCustomCell
        let str: String = tableData.first ?? ""
        cell.theLabel.text = "\(indexPath.row): \(str)"
        // we don't want the cell to use its default selection style
        cell.selectionStyle = .none
        return cell
        
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 30
    }
    
    @objc private func nextVC()  {
//      let controller = FifthOnboardingPageVC()
//      navigationController?.pushViewController(controller, animated: true)
    }
}

and it looks like this:

enter image description here