Search code examples
iosswiftapiuitableviewreloaddata

How can I reinitialize a UITableViewCell in Swift?


I am using a UITableViewCell to show data. When the view loads, the data is empty, however once the api call is complete I want to reinitialize the UITableViewCell so that the data can appear. I am using the following code, but TableView.reloadData() does not reinitialize the UITableViewCell so the data does not reload.

TableViewCell

class TableViewCell: UITableViewCell {
    
    var Info: Information?
    let Chart = LineChartView(frame: .zero)
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        print("initialized")
        self.contentView.addSubview(Chart)
        Chart.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            Chart.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
            Chart.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
            Chart.rightAnchor.constraint(equalTo: self.contentView.rightAnchor, constant: -10),
            Chart.leftAnchor.constraint(equalTo: self.contentView.leftAnchor, constant: 10),
        ])
        let data = ChartHelpers().makeLineChart(data: Info?.Values ?? [Double]())
        Chart.data = data
        self.contentView.layoutIfNeeded()
     }

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

ViewController


class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var Info: Information?
    let TableView = UITableView(frame: .zero)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        APICall()
        setUpUI()
    }

    func setUpUI() {
        view.addSubview(TableView)
        TableView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            TableView.topAnchor.constraint(equalTo: view.topAnchor),
            TableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            TableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            TableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        ])
        self.TableView.register(TableViewCell.self, forCellReuseIdentifier: "Chart")
        TableView.delegate = self
        TableView.dataSource = self
        TableView.reloadData()
        view.layoutIfNeeded()
    }

    func APICall() {
        API().fetchInformation(Name: "John Doe") { (Info) in
            //success connecting to api
            DispatchQueue.main.async {
                self.Info = Info
                self.TableView.reloadData()
            }
        } failure: { (error) in
            //failure connecting to api

            }
        }
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = TableView.dequeueReusableCell(withIdentifier: "Chart", for: indexPath) as! TableViewCell
        tableView.rowHeight = 300
        cell.Info = self.Info
        return cell
    }
}


Solution

  • It's best not to do configuration in the init, as the cell instance may be reused. Setup should be done when dequeuing a cell. This can be done with a setup method, or by setting the properties, as you have done here with Info (by the way, it would be Swifty-er to name that info).

    The easiest way to get what you want here would be to add a didSet to your Info property and call the setup methods there:

    var info: Information? {
        didSet {
            let data = ChartHelpers().makeLineChart(data: Info?.Values ?? [Double]())
            Chart.data = data
            self.contentView.layoutIfNeeded()
        }
    }
    
    

    Although other refactors may be better in the long run.