0

When I tap a button, I want the value to update for only that cell.

I'm a bit of a noob so please be easy on me!

I currently have a delegate set in the custom cell that I'm accessing in the tableViewController as well as Bool values that determine whether the button was pressed or not and if the button should be enabled. The button is by default set as true. I created a column in Parse called isEnabled that gives the button a default value of true.

Currently, when I tap the button every 3rd cell changes (meaning it doesn't send an action for only that 1 cell). However, when printing to the logs, the logs say that the value has changed from true to false, only AFTER i scroll back up...

Why doesn't the value change immediately?

Here is my custom cell code:

protocol CellButtonDelegate {

    func buttonClicked(cell : PFTableViewCell)

}

var delegate : CellButtonDelegate?

class FeedTableViewCell: PFTableViewCell {
     var delegate : CellButtonDelegate?

    internal var buttonEnabled : Bool? {

        get {

            return happyOutlet.enabled

        } set {

            happyOutlet.enabled = newValue!

        }

    }
   @IBOutlet weak var happyOutlet: UIButton!

 @IBAction func happyBtn(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!)"
               // print(votes)
            }

        }

        delegate?.buttonClicked(self)
        viewBlackYellow.backgroundColor = UIColor.yellowColor()

        happyOutlet.enabled = false
        sadOutlet.enabled = false
        happyOutlet.alpha = 0
        sadOutlet.alpha = 0
        //These values all get updated for every 3rd cell

    }

Here is my cellForRowAtIndexPath code (The values for the votes, as well as the other values I have are being cached):

The numberOfRowsInSection appears based on the scroll because the values/cells are being cached as I set the number of cells to return biased on objects which depends on the scroll:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        objectArray = objects //the objects array is cached (using PFQuery function to query the values, so the cells are cached as well)
        return objects!.count
    }


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

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

     objects![indexPath.row]


            if let pfObject = objectArray?[indexPath.row] {

                cell?.parseObject = object
                var votes: Int? = pfObject["votes"] as? Int
                if votes == nil {
                    votes = 0
                }

                cell?.delegate = self


                let isEnabled = pfObject["isEnabled"] as! Int



 if isEnabled == 1 {

                cell?.buttonEnabled = true

            } else  {

                cell?.buttonEnabled = false
                print("its false")

            }
       return cell
     } 

 func buttonClicked(cell: PFTableViewCell) {

        let indexPath = tableView.indexPathForCell(cell)

        var object : PFObject = objectArray![indexPath!.row] as! PFObject
        object["isEnabled"] = 0

        objectArray?.removeAtIndex((indexPath?.row)!)
        objectArray?.insert(object, atIndex: (indexPath?.row)!)
    }

How do I make it so that the cell values and actions don't repeat? I'm assuming that all the logic should not be going in the custom cell but in the tableVC instead. I've tried to implement that however, without any luck (i was sending an action from the happyOutlet.tag).

Lastly, i had the self.tableView.reloadData() / self.tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .None)

Both make the screen jittery because I also have cached video in the cell. It also sometimes (strangely enough) doesn't help. If i can avoid reloading the tableView that would be best.

Any and all suggestions, questions, comments and improvements are immensely appreciated.

I referenced this link as well as others and they have not helped: Implementing a like button in a tableviewCell in swift which didn't help.

Community
  • 1
  • 1
Lukesivi
  • 2,206
  • 4
  • 25
  • 43
  • 1
    in your delegate's buttonClicked method, after updating the data, you need to call, self.tableView.reloadData() to refresh the tableview – Surely Nov 30 '15 at 15:24
  • Hey, thanks for the comment. I added it, and it did help. However, I added this before and the problem is I also have a video running and a few other things, so it makes the UI jittery. Is there a way around that or a way to not use the `reloadData()`? @zp_x – Lukesivi Nov 30 '15 at 15:31
  • 1
    " I want the value to update for only that cell", if this is the case, you dont need a delegate, you can directly update the value inside the cell class. – Surely Nov 30 '15 at 15:34
  • When I do that, I still have every 3rd cell updating. I'm caching the Videos, votes, etc, so the cells load based on the cache. – Lukesivi Nov 30 '15 at 15:35
  • can you post your viewcontroller's buttonClicked method, i am not quite sure what you are doing at the moment. The "happyOutlet" is the happyBtn right? so you can disable or enable it with "sender.enabled=false" – Surely Nov 30 '15 at 15:36
  • Just updated. The happyOutlet is the outlet to the button, so yes. Where can i disable it? In a function? Should i be disabling it in the custom cell? – Lukesivi Nov 30 '15 at 15:42
  • yeah, in this function "@IBAction func happyBtn(sender: AnyObject)", you can cast the sender to UIButton and disable it by "sender.enabled=false" – Surely Nov 30 '15 at 15:44
  • I'm doing that with `happyOutlet.enabled = false` as well as other things, but they all get updated for every 3rd cell. – Lukesivi Nov 30 '15 at 15:46
  • @zp_x i just added the `numberOfRowsInSection` which shows that the cells are being cached actually and appears based on the the scroll/when they're accessed because `objects` is a cached Parse array. – Lukesivi Nov 30 '15 at 15:55
  • hi, how do you connect the happyOutlet, it cant be seen from the cell class? – Surely Nov 30 '15 at 16:00
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/96573/discussion-between-lukesivi-and-zp-x). – Lukesivi Nov 30 '15 at 16:01

1 Answers1

0

It would be more MVC to move your logic to your view controller. Make your button as a public IBOutlet so in your cellForRowAtIndexPath you can say, happyOutlet.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)

Then say:

func buttonClicked(sender:UIButton) {
    let location = sender.convertPoint(sender.bounds.origin, toView: self.tableView)

    let indexPath = self.tableView.indexPathForRowAtPoint(location)

    var object : PFObject = objectArray![indexPath!.row] as! PFObject
    object["isEnabled"] = 0

    objectArray?.removeAtIndex((indexPath?.row)!)
    objectArray?.insert(object, atIndex: (indexPath?.row)!)

    if let cell:CustomTableViewCell = self.tableView.cellForRowAtIndexPath(indexPath) as? CustomTableViewCell {
    //update your UI
    }
}

You should also remember that when you dequeue a cell, its UI will match its last state. So if you say something like:

        cell.happyOutlet.enabled = false
        cell.happyOutlet.alpha = 0
        cell.viewBlackYellow.backgroundColor = UIColor.yellowColor()

In an action. Then in you cellForRowAtIndexPath after you dequeue the cell you'll want to say something like:

        cell.happyOutlet.enabled = true
        cell.happyOutlet.alpha = 1.0
        cell.viewBlackYellow.backgroundColor = UIColor.whiteColor()//Or whatever your default background color is

To reset the cell to its default values. I'm guessing you can have approximately three cells on screen at any one time, so thinking this why ever third is affected by button press.

beyowulf
  • 15,101
  • 2
  • 34
  • 40
  • So I also need to access the custom cell from this function? – Lukesivi Nov 30 '15 at 15:59
  • Are you asking me or telling me? The last line of code that I wrote will give you a reference to the cell, so you can update it's UI or perform other tasks with it. What I'm really saying is you don't need to handle the button press in your uitableview subclass you can handle it in your view controller. And you can to it without tags, which typically get confusing, if you say let indexPath = self.tableView.indexPathForRowAtPoint(location) – beyowulf Nov 30 '15 at 16:04
  • Ok thanks. Trying this out now. Just confused about what is `sender` defined as? What do I set that as? – Lukesivi Nov 30 '15 at 16:05
  • With a UIControl any target with an action signature with a colon will include the control as sender. – beyowulf Nov 30 '15 at 16:07
  • I'm still getting the repeated values in the cells. I can send a gist if you're interested... The MVC way is definitely the way to go, but i don't understand why it's still updating the cell for every 3rd cell after clicking 1 cell. – Lukesivi Nov 30 '15 at 16:16
  • Hey @beyowulf what do you think? – Lukesivi Nov 30 '15 at 20:59
  • Hey @beyowulf just read the updated answer. I can only see 1 cell at a time (cells are pretty big because i have a video). The problem with all the querying is that the cell's and the values in the cells are cached. my `numberOfRowsInSection` (above) is objects.count, but the objects array is a cached Parse array (i'm using ParseUI). So the issue is fighting against the cache, whether I'm scrolling up or down. Hope that helps a little in explaining it further. – Lukesivi Nov 30 '15 at 21:05
  • I think you're updating the server-side model and the local store of that model, so I'm not sure what the issue is other than: you need to reset the dequeued cell to the default configuration then check if it's been liked and update accordingly as I hinted at above. No need for a bunch of server calls. – beyowulf Nov 30 '15 at 23:01
  • Made progress: 1 cell updates. Problem is that when I click another cell, I get a cross "expectedly found nil when unwrapping optional value" on this line: `let cell:FeedTableViewCell = self.tableView.cellForRowAtIndexPath(indexPath!) as! FeedTableViewCell` @beyowolf – Lukesivi Dec 01 '15 at 13:30
  • I would say try print(self.tableView.cellForRowAtIndexPath(indexPath!)) before the let, so you can see what that cell is and why it's not casting to CustomTableViewCell, then change the let to if let cell:CustomTableViewCell = self.tableView.cellForRowAtIndexPath(indexPath) as? CustomTableViewCell { //update your UI } to guard against unwrapping nil. You might want to print(indexPath) just to make sure that not nil as well. – beyowulf Dec 01 '15 at 14:40
  • @lukeslvi sorry I made a mistake, you need to convert the button's location into your tableview's coordinates like: let location = sender.convertPoint(sender.bounds.origin, toView: self.tableView) think that's why you're getting nil. Sorry about that. – beyowulf Dec 01 '15 at 15:55