swiftuitableviewcustom-cellprepareforreuse

Values Repeat When Scrolling in TableView


When I tap a button in a custom cell and then scroll down (or up) another cell button is also tapped. I see that it's tapped because the button outlet that I created for the button is disabled.

My cellForRowAtIndexPath has a reuseIdentifier for the cell:

var cell: FeedTableViewCell? = tableView.dequeueReusableCellWithIdentifier("MusicCell") as? FeedTableViewCell

Considering I have the degueueReusableCellWithId in the cellForRowAtIndexPath do I need a prepareForReuse? When I add the prepareForReuse in my custom cell file, the cell just goes back to the default values (obviously because I reset it to the default values). Problem is I want it to keep the value of each indexPath.row.

This is how I'm querying the values:

 override func queryForTable() -> PFQuery {
        var query:PFQuery = PFQuery(className:"Music")

        if(objects?.count == 0)
        {
            query.cachePolicy = PFCachePolicy.CacheThenNetwork
        }

        query.orderByAscending("videoId")

        return query
    }

This is the numberOfRowsInSection and cellForRowAtIndexPath

 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
     return  objects!.count
    }


   override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {

        var cell: FeedTableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? FeedTableViewCell
        if(cell == nil) {
            cell = NSBundle.mainBundle().loadNibNamed("FeedTableViewCell", owner: self, options: nil)[0] as? FeedTableViewCell
        }
        if let pfObject = object {
               //I took out the irrelevant methods. I can add them if that makes a difference... 
                var votes:Int? = pfObject["votes"] as? Int
                if votes == nil {
                    votes = 0
        }
        cell?.votesLabel?.text = "\(votes!)"

}

I'm registering this in the viewDidLoad above the super.viewDidLoad()

 tableView.registerNib(UINib(nibName: "FeedTableViewCell", bundle: nil), forCellReuseIdentifier: cellIdentifier)

This is my button query in the customCell:

@IBAction func heartButton(sender: AnyObject) {
        if(parseObject != nil) {
            if var votes:Int? = parseObject!.objectForKey("votes") as? Int {
                votes!++

                parseObject!.setObject(votes!, forKey: "votes")
                parseObject!.saveInBackground()

                votesLabel?.text = "\(votes!)"
            }
        }
        heartOutlet.enabled = false
}

Any help and suggestions mean a lot.

Thank you.


REFRENCE LINKS I USED:

I referred to several links but they were in objective-c and didn't help:

UICollectionView Tap Selects More Than One Cell

How to use prepareForReuse method

I also referred to the docs, and that didn't help much.


Solution

  • From the code you have posted, it is clear that you are not setting the enabled property of the UIButton with respect to the DataSource(The array and its objects you are using to load the tableview, that is the elements in objects array). Whatever objects that array contains, add a property to determine if the condition for the button should be true or false, and then in cellForRowAtIndexPath set the enabled property of the button according to that. When the button is clicked, add a callback to the ViewController(using a delegate) and set the property there.

    Sample Code

    In custom cell class:

    protocol CellButtonDelegate
    {
        func buttonClicked(cell : PFTableViewCell)
    }
    
    public var delegate : CellButtonDelegate?
    
    public var buttonEnabled : Bool?
    {
        get
        {
            return heartOutlet.enabled
        }
        set
        {
            heartOutlet.enabled = newValue
        }
    }
    
    @IBAction func heartButton(sender: AnyObject) {
            if(parseObject != nil) {
                if var votes:Int? = parseObject!.objectForKey("votes") as? Int {
                    votes!++
    
                    parseObject!.setObject(votes!, forKey: "votes")
                    parseObject!.saveInBackground()
    
                    votesLabel?.text = "\(votes!)"
                }
            }
            delegate?.buttonClicked(self)
    }
    

    In ViewController:

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
    
            var cell: FeedTableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? FeedTableViewCell
            if(cell == nil) {
                cell = NSBundle.mainBundle().loadNibNamed("FeedTableViewCell", owner: self, options: nil)[0] as? FeedTableViewCell
            }
            if let pfObject = object {
                   //I took out the irrelevant methods. I can add them if that makes a difference... 
                    var votes:Int? = pfObject["votes"] as? Int
                    if votes == nil {
                        votes = 0
            }
            cell?.buttonEnabled = objects[indexPath.row].isEnabled //The new property you need to add. true by default
            cell?.delegate = self //Make sure you implement the delgate
            cell?.votesLabel?.text = "\(votes!)"    
            return cell?
    }
    
    func buttonClicked(cell : PFTableViewCell)
    {
         //Here, get the indexPath using the cell and assign the new property in the array.
    }
    

    Please note that the above code is rough. Just get the idea from the code and implement it as per your requirement.