0

In my swift app I have a UITableView with one static cell and many dynamic cells.

Static cell contains different fields, such as labels, map (taken from MapKit) and a button, that indicates whether user voted up or not.

Now, when user presses the button, I want to change its color, possibly without refreshing anything else.

So far my code looks like this:

var currentUserVote:Int = 0

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

    if (indexPath as NSIndexPath).row == 0 {
        let cell = tview.dequeueReusableCell(withIdentifier: "cellStatic") as! VideoDetailsCell

        fetchScore(cell.score)

        let voteUpImage = UIImage(named: "voteUp");
        let tintedVoteUpImage = voteUpImage?.withRenderingMode(UIImageRenderingMode.alwaysTemplate)
        cell.voteUpButton.setImage(tintedVoteUpImage, for: UIControlState())

        checkUsersVote() { responseObject in

            if(responseObject == 1) {

                cell.voteUpButton.tintColor = orangeColor

            } else if (responseObject == -1){
                cell.voteUpButton.tintColor = greyColor

            } else {
                cell.voteUpButton.tintColor = greyColor

            } 

            self.currentUserVote = responseObject
        }

        //map handling:
        let regionRadius: CLLocationDistance = 1000

        let initialLocation = CLLocation(latitude: latitude, longitude: longitude)

        centerMapOnLocation(initialLocation, map: cell.mapView, radius: regionRadius)
        //cell.mapView.isScrollEnabled = false
        cell.mapView.delegate = self
        .
        .
        . 
        return cell
        } else {
        //handle dynamic cells
        }
}

So in the method above I'm checking if user voted already and based on that I'm setting different color on the button. I'm also centering the map on a specific point.

Now, since it's a static cell, I connected IBAction outlet to that button:

@IBAction func voteUpButtonAction(_ sender: AnyObject) {
     if(currentUserVote == 1) {
        self.vote(0)
    }else if (currentUserVote == -1){
        self.vote(1)
    } else {
        self.vote(1)
    }
}

and the vote method works as follows:

func vote(_ vote: Int){

    let indexPath = IndexPath(row: 0, section: 0)

    let cell = tview.dequeueReusableCell(withIdentifier: "cellStatic") as! VideoDetailsCell


    switch(vote) {
    case 1:
        cell.voteUpButton.tintColor = orangeColor
    case 0:
        cell.voteUpButton.tintColor = greyColor
    case -1:
        cell.voteUpButton.tintColor = greyColor
    default:
        cell.voteUpButton.tintColor = greyColor
    }

    tview.beginUpdates()
    tview.reloadRows(at: [indexPath], with: .automatic)
    tview.endUpdates()

    currentUserVote = vote

    //sending vote to my backend
}

My problem is, that when user taps the button, he invokes the method vote, then - based on the vote, the button changes color, but immediately after that method cellForRow is called and it changes the color of the button again. Also, it refreshes the map that's inside of it.

What I want to achieve is that when user taps the button, it should immediately change its color and that's it. Map stays untouched and the button is not changed again from cellForRow.

Is there a way of refreshing only that particular button without calling again cellForRow?

user3766930
  • 5,629
  • 10
  • 51
  • 104

3 Answers3

2
  1. First of all, you confuse static and dynamic cells. You can use static cells only in the UITableViewController and you can't use static and dynamic cell at the same time.
  2. I strongly recommend you not to use cell for storing map and button. All elements from the cell will be released after scrolling it beyond the screen.

I can advise you use TableViewHeaderView for this task. In this case you will be able set button and map view as @IBOutlet.

(See more about adding tableview headerView. You can also set it from interface builder.)

Another way is change tableView.contentInset and set your view with map and button as subview to tableView. This method is used when you need create Stretchy Headers.

enter image description here

Community
  • 1
  • 1
  • hm, so after hitting the button, do I have to refresh the `headerView` ? – user3766930 Feb 07 '17 at 21:49
  • No, you can relate `vote` button as `@IBOutset` to your `viewController` and in `vote` method just set its color/image/state/etc. You can also center mapView in this method. And you don't need reload tableView. – Nick Kibish Feb 07 '17 at 21:58
0

It should be quite easy, simply do it in your button handler. The sender argument there is the button object that caused the action. When you were connecting it from IB there was a dropdown to select sender type, you may have missed it and the whole thing would have been obvious with UIButton type there.

Simply change your handler like this :

@IBAction func voteUpButtonAction(_ sender: UIButton) {
     if(currentUserVote == 1) {
        self.vote(0)
    }else if (currentUserVote == -1){
        self.vote(1)
    } else {
        self.vote(1)
    }
    sender.backgroundColor = yourFavouriteColor
}

Another approach would be to create an IBOutlet for your button, since its from a static cell, and then you would be able to reference it from any place in your view controller.

Losiowaty
  • 7,911
  • 2
  • 32
  • 47
  • but the key thing - if you don't want to update anything other than the button - is remove the lines where you `reloadRows` – Russell Feb 07 '17 at 21:26
  • Yes, I just assumed this was obvious ;) – Losiowaty Feb 07 '17 at 21:26
  • hm @Losiowaty that makes sense! So... There's also a `voteDown` button - can I access it from the `voteUpButtonAction` and change its color from there too? – user3766930 Feb 07 '17 at 21:40
  • Yeas, as I said - you can crate `IBOutlets` for UI elements from static cells (same way as you'd go about crating an `IBAction`, there also should be a dropdown, and an outlet is a default option I think). The instead of `sender` you can use `self.voteUpButton` and `self.voteDownButton` or howevee you choose to name them, sven outside of the button handling methods. – Losiowaty Feb 07 '17 at 23:18
0

In this call:

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

I see it calls checkUsersVote() which I'm guessing should get the updated value set in the vote() call. Could the problem be that you aren't doing this currentUserVote = vote until after reloadRows() is called?

JohnO
  • 41
  • 2