iosswiftuitableviewuikitcustom-cell

Can not add spacing between UITableViewCells


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.


Solution

  • 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:

    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