Search code examples
uitableviewswift3uipickerviewios10

UIPickerView value inside of UITableView(based on DateCell) change detailLabel


enter image description here

I tried to make my PickerView value to show on my UITableView detail Label. So I set in my FormCell.swift

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
  self.detailLabel.text = "\(component)\(row)"
}

It works however, when I click next cell, my detailLabel misses previous value like this.(my default label is "01min 30sec")

enter image description here

Any ideas about this problem? Here is my code about this problem.

// FormCell.swift
import UIKit

class FormCell: UITableViewCell, UIPickerViewDelegate, UIPickerViewDataSource {

    // outlet
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var detailLabel: UILabel!
    @IBOutlet weak var pickerView: UIPickerView!

    // variable
    var isObserving = false
    let minutes = Array(0...59)

    // initialize pickerView delegate, dataSource
    override func awakeFromNib() {
        super.awakeFromNib()
        self.pickerView.delegate = self
        self.pickerView.dataSource = self

    }

    // tableViewCell's height setup
    class var expandedHeight: CGFloat { get { return 200 } }
    class var defaultheight: CGFloat { get { return 44 } }

    func checkHeight() {
        pickerView.isHidden = (frame.height < FormCell.expandedHeight)
    }


    // for pickerView
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return minutes.count
    }
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {

        let pickerLabel = UILabel()
        var titleData = ""
        titleData = "\(minutes[row])"
        pickerLabel.text = titleData
        return pickerLabel
    }
    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        return pickerView.frame.width / 3
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        self.detailLabel.text = "\(component)\(row)"
    }




    // set observer for expanding
    func watchFrameChanges() {
        if !isObserving {
            addObserver(self, forKeyPath: "frame", options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.initial], context: nil)
            isObserving = true;
        }
    }
    func ignoreFrameChanges() {
        if isObserving {
            removeObserver(self, forKeyPath: "frame")
            isObserving = false;
        }
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "frame" {
            checkHeight()
        }
    }

}

my Controller

// AddCircuitVC.swift
import UIKit

class AddCircuitVC: UITableViewController {

    let formCellID = "formCell"
    var selectedIndexPath: IndexPath?


    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 9
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let itemData = dataArray[indexPath.row]
        var cellID = wordCellID

            cellID = formCellID
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as? FormCell
            cell?.titleLabel.text = itemData[titleKey]

            return cell!


    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let previousIndexPath = selectedIndexPath

        if indexPath == selectedIndexPath {
            selectedIndexPath = nil
        } else {
            selectedIndexPath = indexPath
        }

        var indexPaths: Array<IndexPath> = []
        if let previous = previousIndexPath {
            indexPaths += [previous]
        }
        if let current = selectedIndexPath {
            indexPaths += [current]
        }

        if indexPaths.count > 0 {
            tableView.reloadRows(at: indexPaths, with: UITableViewRowAnimation.automatic)
        }

    }

    // observer
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            (cell as! FormCell).watchFrameChanges()
    }
    override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            (cell as! FormCell).ignoreFrameChanges()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        for cell in tableView.visibleCells {
            if cell.isKind(of: FormCell.self) {
                (cell  as! FormCell).ignoreFrameChanges()
            }
        }
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            if indexPath == selectedIndexPath {
                return FormCell.expandedHeight
            } else {
                return FormCell.defaultheight
            }
    }
}

Solution

  • You're only setting your table cell's self.detailLabel.text in your pickerView delegate method.

    So when you scroll your cell off screen and then on-screen again, the table view cell "forgets" what it was set to before.

    You need to modify your cellForRowAt indexPath: table view data source method to return a value for cell.detailLabel.text, and that means your ultimate data source -- the itemData dictionary in your dataArray -- needs to have a new entry for duration set by the picker.

    I'd recommend passing along the itemData dictionary as a property or parameter when the customTableViewCell is created in cellForRowAt indexPath:, and when the user selects a duration, set the dictionary entry and make sure it gets saved / updated in your dataArray.