skip to Main Content

I made a table view with a label that increments and decrements on pressing a button and another button to show the text in another label outside the UItableView. Everything works fine but when I scroll the Tableview the value resets to zero!

Before Scrolling
After Scrolling

My ViewController class

class ViewController: UIViewController{

var numArray = [Value]()
var initialValue = 0


@IBOutlet weak var tableView : UITableView!
@IBOutlet weak var lblOutput : UILabel!


override func viewDidLoad() {
    super.viewDidLoad()
    for _ in 0 ... 100{
        numArray.append(Value(number: initialValue))
    }
    self.lblOutput.text = "(initialValue)"

    tableView.delegate = self
    tableView.dataSource = self
    tableView.reloadData()
    
}
}

 extension ViewController : UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return numArray.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",for: indexPath) as? ControllerTableViewCell else{fatalError("Error in creating cells")}

    cell.delegate = self
    cell.data = numArray[indexPath.row]
    cell.lblInput.text = "(cell.data.number)"
    
    return cell
}
}

extension ViewController : MyTableViewCellDelegate{
  func DidPrint(Data: String) {
    self.lblOutput.text = "(Data)"
}
}

My TableViewCell class

protocol MyTableViewCellDelegate : AnyObject {
   func DidPrint(Data: String)
 }


class ControllerTableViewCell: UITableViewCell {

weak var delegate : MyTableViewCellDelegate?
var data : Value!

private var counterValue = 0

@IBOutlet var lblInput : UILabel!
@IBOutlet var btnPrint : UIButton!
@IBOutlet var btnPlus : UIButton!
@IBOutlet var btnMinus : UIButton!

override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
}


@IBAction func DidPressPrint(){
    self.data.number = counterValue
    delegate?.DidPrint(Data: "(data.number)")
    print(data.number)
}
@IBAction func DidPressPlus(){
    counterValue += 1
    data.number = counterValue
    self.lblInput.text = "(data.number)"
}
@IBAction func DidPressMinus(){
    if(counterValue > 0){
                counterValue -= 1
                data.number = counterValue
            }
    else{
        counterValue = 0
        data.number = 0
    }
    self.lblInput.text = "(data.number)"
}


}

My Data Model

import Foundation

struct Value{
  var number : Int
}

2

Answers


  1. As @El Tomato suggested, you are not updating your data source, that’s why your changes gets "forgotten" on scroll.

    Try to move your didPressPlus, didPressMinus and didPressPrint in your ViewController class and redefine your table view delegate like below.
    By passing the tag attributes to the buttons, you can then retrieve the index of the item pressed in the functions and edit the correct data source item.
    Also remove the unnecessary MyTableViewCellDelegate.

    class ViewController: UIViewController{
    
        var numArray = [Value]()
        var initialValue = 0
    
        @IBOutlet weak var tableView : UITableView!
        @IBOutlet weak var lblOutput : UILabel!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            for _ in 0 ... 100 {
                numArray.append(Value(number: initialValue))
            }
            self.lblOutput.text = "(initialValue)"
    
            tableView.delegate = self
            tableView.dataSource = self
        }
    }
    
    extension ViewController : UITableViewDelegate, UITableViewDataSource 
    {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return numArray.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? ControllerTableViewCell else {fatalError("Error in creating cells")}
            let indexItem = indexPath.row
            let valueItem = numArray[indexItem]
    
            cell.lblInput.text = valueItem.number
    
            cell.btnMinus.tag = indexItem
            cell.btnMinus.addTarget(self, action: #selector(didPressMinus(_:)), for: .touchUpInside)
    
            cell.btnPlus.tag = indexItem
            cell.btnPlus.addTarget(self, action: #selector(didPressPlus(_:)), for: .touchUpInside)
    
            cell.btnPrint.tag = indexItem
            cell.btnPrint.addTarget(self, action: #selector(didPressPrint(_:)), for: .touchUpInside)
    
            return cell
        }
    
        @objc private func didPressPlus(_ sender: UIButton) {
            let dataIndex = sender.tag
            if numArray.count < dataIndex { return }
    
            let numArrayItem = numArray[dataIndex]
    
            if (numArrayItem.number >= 0) {
                numArray[dataIndex].number -= 1
            } 
    
            tableView.reloadData()
        }
    
        @objc private func didPressMinus(_ sender: UIButton) {
            let dataIndex = sender.tag
            if numArray.count < dataIndex { return }
    
            numArray[dataIndex].number += 1
    
            tableView.reloadData()
        }
    
        @objc private func didPressPrint(_ sender: UIButton) {
            let dataIndex = sender.tag
            if numArray.count < dataIndex { return }
    
            self.lblOutput.text = "(numArray[dataIndex].number)"
        }
    }
    

    In order to move the three methods in the ViewController you’ll need to remove the two correspondent IBAction from the UITableViewCell class.
    Also, remove the linkage with the ControllerTableViewCell actions.
    Here is the resulting ControllerTableViewCell:

    class ControllerTableViewCell: UITableViewCell {
    
        @IBOutlet var lblInput : UILabel!
        @IBOutlet var btnPrint : UIButton!
        @IBOutlet var btnPlus : UIButton!
        @IBOutlet var btnMinus : UIButton!
    
        override func awakeFromNib() {
            super.awakeFromNib()
        }
    }
    
    Login or Signup to reply.
  2. Your TableView’s numberOfRowsInSection is using numArray as a source (numArray.count) and so is your cellForRowAt function, but your cell functions are updating your ‘data’ variable. Your ‘data’ variable is locally defined to your tableView and gets reset every time it is activated (including when you scroll).
    You need to update the numArray or some other global resource to make it work. This involves using indexpath of the cell value inside the cell functions, meaning you need a way to refer to indexPath inside the cell. This article explains how to use tags or delegates, https://fluffy.es/handling-button-tap-inside-uitableviewcell-without-using-tag/.

    Here’s a solution using the existing delegate.

    import UIKit
    import Foundation
    
    var initialValue = 0
    var numArray = Array(repeating: initialValue, count: 100)
    
    class ViewController: UIViewController {
        @IBOutlet weak var tableView: UITableView!
        @IBOutlet weak var lblOutput: UILabel!
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
               self.lblOutput.text = "(initialValue)"
    
               tableView.delegate = self
               tableView.dataSource = self
               tableView.reloadData()
            // Do any additional setup after loading the view.
        }
    
    
    }
    
    extension ViewController : UITableViewDelegate,UITableViewDataSource{
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return numArray.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",for: indexPath) as? ControllerTableViewCell else{fatalError("Error in creating cells")}
            cell.indexPath = indexPath
            cell.delegate = self
            cell.lblInput.text = String(numArray[indexPath.row])
            return cell
        }
        
      
    }
    
    extension ViewController : MyTableViewCellDelegate{
        
        func DidPrint(Data: String) {
            self.lblOutput.text = "(Data)"
        }
    }
    
    protocol MyTableViewCellDelegate : AnyObject {
        func DidPrint(Data: String)
     }
    
    
    class ControllerTableViewCell: UITableViewCell {
    
    weak var delegate : MyTableViewCellDelegate?
    var indexPath : IndexPath? 
    
    private var counterValue = 0
    
    @IBOutlet var lblInput : UILabel!
    @IBOutlet var btnPrint : UIButton!
    @IBOutlet var btnPlus : UIButton!
    @IBOutlet var btnMinus : UIButton!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }
    
    
    @IBAction func DidPressPrint(){
        delegate?.DidPrint(Data: "(numArray[indexPath!.row])")
    }
    @IBAction func DidPressPlus(){
        numArray[indexPath!.row]  = numArray[indexPath!.row] + 1
        self.lblInput.text = "(numArray[indexPath!.row])"
    }
    @IBAction func DidPressMinus(){
        if(numArray[indexPath!.row] > 0){
            numArray[indexPath!.row] = numArray[indexPath!.row] - 1
        }
        else{
            numArray[indexPath!.row] = 0
        }
        self.lblInput.text = "(numArray[indexPath!.row])"
    }
    
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search