简体   繁体   中英

Swift: Change text color of all UITableViewCells when one cell is tapped

I have a UITableView in Swift for iOS that is supposed to allow only one cell to be selected at a time. I have it set up so that taps toggle between selected and not selected, and a long press sets it to permanently selected, which means it starts out selected by default when the view loads. I was having difficulty with keeping the cells' label colored when the cell went outside the bounds of the visible part of the scrollview and then reentered it, so I used my data model to keep track of what cells were selected and permanently selected. By doing so, I have bypassed Apple's way of making a tableview allow only single cell selection by selecting "Single Selection" in interface builder. Now I am trying to build out my own functionality to change the color of all cells visible on the screen when another cell is tapped. I am currently attempting to do this by looping through each cell when it is tapped and calling a method on that custom cell that will change the text color. The problem is, it isn't working. If I move the cells out of the visible scrolling area and then back in, the cells will be colored properly, but if I select one cell, and then another, the first cell won't change color while it is visible.

Here is my code. I have removed some code that was irrelevant, so if there is something else you would like to see, let me know.

import Foundation
import UIKit

class AddDataViewController : UIViewController {

    @IBOutlet weak var locationTableView: UITableView!

    let locationTableViewController = LocationTableViewController()

    override func viewWillAppear(animated: Bool) {
        // Set the data source and delgate for the tables
        self.locationTableView.delegate = self.locationTableViewController
        self.locationTableView.dataSource = self.locationTableViewController

        // Refresh the tables
        self.locationTableView.reloadData()

        // Add the table view controllers to this view
        self.addChildViewController(locationTableViewController)
    }

    override func viewDidLoad(){

        super.viewDidLoad()

        // Create a tap gesture recognizer for dismissing the keyboard
        let tapRecognizer = UITapGestureRecognizer()

        // Set the action of the tap gesture recognizer
        tapRecognizer.addTarget(self, action: "dismissKeyboard")

        // Update cancels touches in view to allow touches in tableview to be detected
        tapRecognizer.cancelsTouchesInView = false

        // Add the tap gesture recognizer to the view
        self.view.addGestureRecognizer(tapRecognizer)
    }

}

class LocationTableViewController : UITableViewController, UITableViewDelegate, UITableViewDataSource {

    var locationSelected : String = ""

    func colorAllCells() {
        for section in 0 ... self.tableView.numberOfSections() - 1 {
            for row in 0 ... self.tableView.numberOfRowsInSection(section) - 1 {
                if row != 0 {
                    let indexPath = NSIndexPath(forRow: row, inSection: section)
                    let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! PlayerLocationCell
                    cell.indexPath = indexPath.row - 1
                    cell.setCellColor()
                }
             }
        }
    }

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row != 0 {
            // Color all of the cells
            self.colorAllCells()
        }
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = PlayerLocationCell(style: UITableViewCellStyle.Value2, reuseIdentifier: "addDataCell")

        cell.selectionStyle = UITableViewCellSelectionStyle.None       

        if indexPath.row != 0 {
            cell.textLabel!.text = pokerLibrary.currentLog.locationList[indexPath.row - 1].name
            cell.indexPath = indexPath.row - 1
            cell.setCellColor()
        }

        return cell
    }
}

class PlayerLocationCell : UITableViewCell {

    let selectedColor = UIColor.greenColor()
    let deselectedColor = UIColor.grayColor()
    let permanentlySelectedColor = UIColor.blueColor()
    var shouldHighlightText : Bool = true
    var indexPath : Int = 0

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: "cellPressed")
        longPressRecognizer.minimumPressDuration = 1.0
        longPressRecognizer.cancelsTouchesInView = false
        self.addGestureRecognizer(longPressRecognizer)

        let tapRecognizer = UITapGestureRecognizer(target: self, action: "cellTapped")
        tapRecognizer.cancelsTouchesInView = false
        self.addGestureRecognizer(tapRecognizer)
    }

    func setCellColor() {
        if shouldHighlightText {
            if self.isPermanentlySelected() {
                self.textLabel!.textColor = self.permanentlySelectedColor
            }
            else if self.isCurrentlySelected() {
                self.textLabel!.textColor = self.selectedColor
            }
            else {
                self.textLabel!.textColor = self.deselectedColor
            }
        }
    }

    func cellTapped() {
        if shouldHighlightText {
            self.setPermanentlySelected(false)

            if self.isCurrentlySelected() {
                self.setSelectedProperty(false)
                self.setCellColor()
            }
            else {
                self.setSelectedProperty(true)
                self.setCellColor()
            }
        }
    }

    func cellPressed() {
        if self.shouldHighlightText {
            if !self.isPermanentlySelected() {
                self.setPermanentlySelected(true)
                self.setCellColor()
            }
        }
    }

    func setPermanentlySelected(b : Bool) {
        if b {
            // Set all other locations to false
            for i in 0 ... pokerLibrary.currentLog.locationList.count - 1 {
                pokerLibrary.currentLog.locationList[i].selected = false
                pokerLibrary.currentLog.locationList[i].permanentlySelected = false
            }
        }
        pokerLibrary.currentLog.locationList[self.indexPath].permanentlySelected = b
    }

    func isPermanentlySelected() -> Bool {
        return pokerLibrary.currentLog.locationList[self.indexPath].permanentlySelected
    }

    func setSelectedProperty(b : Bool) {
        if b {
            // Set all other locations to false
            for i in 0 ... pokerLibrary.currentLog.locationList.count - 1 {
                pokerLibrary.currentLog.locationList[i].selected = false
                pokerLibrary.currentLog.locationList[i].permanentlySelected = false
            }
        }
        pokerLibrary.currentLog.locationList[self.indexPath].selected = b
    }

    func isCurrentlySelected() -> Bool {
        return pokerLibrary.currentLog.locationList[self.indexPath].selected
    }

}

As you can see, I am currently calling the LocationTableViewController's colorAllCells method from inside the didSelectRowAtIndexPath method. The colorAllCells method loops through all cells and calls each cell's setCellColor method, which will determine what the cell color should be and set the text color. I have verified each of the cell's is having its setCellColor method called from the loop, and it is correctly determining which color to set. The color just won't change. I have also tried calling self.tableView.reloadData() after I have looped through each cell and set the color, but that didn't work either.

I would appreciate any help with this issue. If you need me to clarify anything, let me know. Thanks!

I finally found a solution. If I remove the reference to self before calling tableView.cellForRowAtIndexPath, and I move the code from the colorAllCells method into the didSelectRowAtIndexPath method, then it works.

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if indexPath.row != 0 {
        // Color all of the cells
        for section in 0 ... self.tableView.numberOfSections() - 1 {
            for row in 0 ... self.tableView.numberOfRowsInSection(section) - 1 {
                if row != 0 {
                    let indexPath = NSIndexPath(forRow: row, inSection: section)
                    if let cell = tableView.cellForRowAtIndexPath(indexPath) as? PlayerLocationCell {
                        cell.setCellColor()
                    }
                }
            }
        }
    }
}

This solution works. However, I don't understand why it works when I place the code inside the didSelectRowAtIndexPath method, but if I take that code out and put it in its own function and then call that function, it doesn't work. Does anyone have any idea why that is?

You need some way to manage the state of cell selection in your view controller. I would do it with a Set, like this:

class LocationTableViewController: UITableViewController {

    var selectedIndexPaths = Set<NSIndexPath>()

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

        self.selectedIndexPaths.insert( indexPath )

        if let cell = tableView.cellForRowAtIndexPath( indexPath ) as? PlayerLocationCell {
            // Change color of the existing cell
        }
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        if let cell = tableView.dequeueReusableCellWithIdentifier( "addDataCell" )as? PlayerLocationCell {

            if self.selectedIndexPaths.contains( indexPath ) {
                // Change color of the cell just dequeued/created
            }
            return cell
        }
        fatalError( "Could not dequeue cell of type 'PlayerLocationCell'" )
    }
}

Also, you should use dequeueReusableCellWithIdentifier: to reuse table view cells and not create new ones in your tableView:cellForRowAtIndexPath method.

Try calling your colorAllCells() function on the main thread.

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            if indexPath.row != 0 {

                // Color all of the cells
                dispatch_async(dispatch_get_main_queue(),{
                    self.colorAllCells()
                 })

            }
        }

This will however block your UI.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM