nm1213
nm1213

Reputation: 99

Background colour changes on multiple tableviewcells when there are more than 7 rows in the table

I am reading data from Firebase and then displaying this data in tableview cells. There is also a file stored locally that contains the read/unread status for each cell. The file contains the key of the each child in Firebase, and I match this with the data in each cell.

Basically, if the cell has a key that is also found in the file, then this data is considered "read" by the user. However, if the cell has a key that has no equivalent in the local file, then this data is considered "unread".

So far this works perfectly. I have then added two swipe functions for each cell. First to mark as read, and also mark as unread. When marking as "read", the swipe invokes a function that writes the key for the cell to the local file. Conversely, when marking as "unread", the swipe invokes another function that removes the key from the local file. This also works perfectly.

However, on swipe, I also have to change the cell background colour to reflect "read" or "unread". This causes a problem because the cells are reloaded for reuse... in my case, I get 7 cells before it reloads.

This means that if I have more than 7 cells, and I mark something as "read", I end up changing the background colour of more than one cell. The file writing stuff works fine... just the background colour is duplicated.

I change the colour in the editActionsForRowAt function as follows:

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

    let Action2 = UITableViewRowAction(style: .default, title: "Mark Read", handler: { (action, indexPath) in

// function goes on to do stuff before setting the colour...

    if filteredMessageIDforSwipe.count == 0  {

    let selectedCell = self.Nomination2TableView.cellForRow(at: indexPath) as! Nomination2TableViewCell

    selectedCell.contentView.backgroundColor = UIColor(hexString: "#ffffff")

Note that I am not selecting a cell... rather swiping, and then trying to change the colour of the cell background for the cell that is swiped.

Obviously I only want the cell I swiped on to change colour, and not another cell (if I have more than 7 cells).

Thanks!

N.

EDIT: I've modified editActionsForRowAt as recommended below, but without effect.

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

    selectedRowIndexForReadUnread = indexPath.row

    let Action2 = UITableViewRowAction(style: .default, title: "Mark Read", handler: { (action, indexPath) in

    if filteredMessageIDforSwipe.count == 0 && selectedRowIndexForReadUnread = indexPath.row   {

    let selectedCell = self.Nomination2TableView.cellForRow(at: indexPath) as! Nomination2TableViewCell

    selectedCell.contentView.backgroundColor = UIColor(hexString: "#ffffff")

Edit: For reference, here is the editActionsForRowAt function... Action2 and Action5 are relevant.

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

    selectedRowIndexForReadUnread = indexPath.row

    if tableView == self.Nomination2SearchTableView {


        let Action1 = UITableViewRowAction(style: .default, title: "Select", handler: { (action, indexPath) in

            self.Nomination2SearchTableView.isHidden = true
            self.Nomination2TableView.isHidden = false
            NominationState = "Active"

            let SearchObject: Nomination2SearchModel
            SearchObject = Nomination2SearchList[indexPath.row]

            userIDStringforNominationSearch = SearchObject.UserIDString!
            let FullName = SearchObject.Full_Name

            self.FullNameLabel.isHidden = false
            self.FullNameLabel.text = "Showing " + FullName! + "'s Shout Outs"

            self.LoadDataFromNominationafterSearch()
            self.ActiveButton.setTitleColor(.orange, for: .normal)


            let banner = NotificationBanner(title: "Tip", subtitle: "Swipe to select a user.", style: .info)
            banner.haptic = .none
            banner.show()

            //We are setting the editor mode to the opposite of what is displayed in the tableview, because EditorMode text is what is displayed when you swipe the cell.
           // EditorMode = "Set to Complete"

        })
        return [Action1]

    }

    if tableView == self.Nomination2TableView{


        if NominationState == "Active" {


                     let Action2 = UITableViewRowAction(style: .default, title: "Mark Read", handler: { (action, indexPath) in

                        let searchObject9: Nomination2Model
                        searchObject9 = Nomination2List[indexPath.row]
                        let messageIDKey = searchObject9.key


                        let filename = self.getDocumentsDirectory().appendingPathComponent("output.txt")
                        do {

                            let textStringFromFile = try String(contentsOf: filename, encoding: .utf8)

                            var result: [(messageID: String, readStatus: String)] = []

                            // var indexCount = result.count

                            let rows = textStringFromFile.components(separatedBy: "\n")
                            for row in rows {
                                let columns = row.components(separatedBy: ",")
                                result.append((columns[0], columns[1]))
                            }

                            filteredMessageIDforSwipe = result.filter { $0.messageID == (messageIDKey) }

                        }
                        catch{

                        }

                        if filteredMessageIDforSwipe.count == 0   {

                        let selectedCell = self.Nomination2TableView.cellForRow(at: indexPath) as! Nomination2TableViewCell
                            // let selectedCellForReadMessages:UITableViewCell = self.Nomination2TableView.cellForRow(at: indexPath)! as! Nomination2TableViewCell
                        selectedCell.contentView.backgroundColor = UIColor(hexString: "#ffffff")

                        // let myReadCell = self.Nomination2TableView.cellForRow(at: indexPath) as! Nomination2TableViewCell
                        // myReadCell.NominationNameLabel.textColor = UIColor.lightGray
                        // myReadCell.NominationTextLabel.textColor = UIColor.lightGray

                        let NominationObject: Nomination2Model
                        NominationObject = Nomination2List[indexPath.row]
                        nominationKeyForWhenCellSelected = NominationObject.key
                        self.writeFile()
                        // self.removeRowFromFile()
                        }

                        if filteredMessageIDforSwipe.count > 0 {

                            let alert = UIAlertController(title: "Failure",
                                                          message: "Shout-out is already read!",
                                                          preferredStyle: .alert)
                            alert.addAction(UIAlertAction(title: "OK", style: .default))
                            self.present(alert, animated: true, completion: nil)

                        }

            })



            let Action5 = UITableViewRowAction(style: .default, title: "Mark Unread", handler: { (action, indexPath) in

                let searchObject9: Nomination2Model
                searchObject9 = Nomination2List[indexPath.row]
                let messageIDKey = searchObject9.key


                let filename = self.getDocumentsDirectory().appendingPathComponent("output.txt")
                do {

                    let textStringFromFile = try String(contentsOf: filename, encoding: .utf8)

                    var result: [(messageID: String, readStatus: String)] = []

                    // var indexCount = result.count

                    let rows = textStringFromFile.components(separatedBy: "\n")
                    for row in rows {
                        let columns = row.components(separatedBy: ",")
                        result.append((columns[0], columns[1]))
                    }




                    filteredMessageIDforSwipe = result.filter { $0.messageID == (messageIDKey) }

                }
                catch{

                }

                if filteredMessageIDforSwipe.count > 0 {


                    let selectedCell:UITableViewCell = tableView.cellForRow(at: indexPath)!
                    selectedCell.contentView.backgroundColor = UIColor(hexString: "#fcf0e5")

                    // let myCell = self.Nomination2TableView.cellForRow(at: indexPath) as! Nomination2TableViewCell
                    // myCell.NominationNameLabel.textColor = UIColor.orange
                    // myCell.NominationTextLabel.textColor = UIColor.black

                    let NominationObject: Nomination2Model
                    NominationObject = Nomination2List[indexPath.row]
                    nominationKeyForWhenCellSelected = NominationObject.key
                    // self.writeFile()
                    self.removeRowFromFile()
                }

                if filteredMessageIDforSwipe.count == 0 {

                    let alert = UIAlertController(title: "Failure",
                                                  message: "Shout-out is already unread!",
                                                  preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default))
                    self.present(alert, animated: true, completion: nil)

                }

            })

            Action2.backgroundColor = UIColor.lightGray
            Action5.backgroundColor = UIColor.blue
            return[Action2, Action5]
        }



     if NominationState == "Approve"{

     let Action3 = UITableViewRowAction(style: .default, title: NominationState, handler: { (action, indexPath) in


            let searchObject9: Nomination2Model
            searchObject9 = Nomination2List[indexPath.row]

            ForName  = searchObject9.ForName!
            FromName = searchObject9.FromName!
            ForWhat = searchObject9.ForWhat!
             key = searchObject9.key!
             ForUserID = searchObject9.ForUserID!
             FromUserID = searchObject9.FromUserID!
             PointsGifted = searchObject9.PointsGifted!
             NominationText = searchObject9.NominationText!
            ImageNameNominations = searchObject9.ImageNameString!
            ImageURLNominations = searchObject9.ImageURL!


            databaseReference = Database.database().reference()

            databaseReference.child("Users").child(FromUserID).observeSingleEvent(of: DataEventType.value, with: { (snapshot) in

                let value = snapshot.value as? NSDictionary

                let CurrentPointsRedeemed = value?["Points_Redeemed"] as? Int
                let CurrentPointsBalance = value? ["Points_Balance"] as? Int

                let NewPointsRedeemed = CurrentPointsRedeemed! + PointsGifted

                let NewPointsBalance = CurrentPointsBalance! - PointsGifted



                if NewPointsBalance >= 0{

                    databaseReference.child("Users").child(FromUserID).updateChildValues(["Points_Redeemed": NewPointsRedeemed])

                    databaseReference.child("Users").child(FromUserID).updateChildValues(["Points_Balance": NewPointsBalance])



                    self.postToFirebase()
                    self.giftPoints()
                    self.pendingCount()

                    Nomination2List.remove(at: indexPath.row)
                    tableView.deleteRows(at: [indexPath], with: .fade)

                }

                if NewPointsBalance <= 0{

                    let alert = UIAlertController(title: "Nominations",
                                                  message: "Not Enough Points for approval",
                                                  preferredStyle: .alert)

                    alert.addAction(UIAlertAction(title: "OK", style: .default))

                    self.present(alert, animated: true, completion: nil)


                }

        })


     })

        Action3.backgroundColor = UIColor.blue
        return[Action3]

        }
        //here

        //Action2.backgroundColor = UIColor.blue
        //return[Action2]
     else{
        return [UITableViewRowAction]()
        }
    }
    else {
        return [UITableViewRowAction]()
    }

}

Upvotes: 0

Views: 541

Answers (4)

Ashvini
Ashvini

Reputation: 352

You can manage this by using didSelectRow and cellForRow delegate methods of UITableView. Use one variable for managing selected row index. Use below code:

var selectedRowIndex:Int = -1

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {       
        let cell = self.Nomination2TableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as Nomination2TableViewCell!
        if self.selectedRowIndex == indexPath.row {
            cell.contentView.backgroundColor = UIColor(hexString: "#efefef")
        } else {
            cell.contentView.backgroundColor = UIColor(hexString:"defaultColor")
        }    
        return cell
    }

 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.selectedRowIndex = indexPath.row            
        self.Nomination2TableView.reloadRows(at:[indexPath], with: .none)
    }

As you edited your question, you can try below method:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
DispatchQueue.main.async {
    self.Nomination2TableView.beginUpdates()    
    self.selectedRowIndex = indexPath.row            
    self.Nomination2TableView.reloadRows(at:[indexPath], with: .none)
    self.Nomination2TableView.endUpdates()      
}

    let Action2 = UITableViewRowAction(style: .default, title: "Mark Read", handler: { (action, indexPath) in

Upvotes: 0

lajosdeme
lajosdeme

Reputation: 2407

Table view cells get reused this is why the background color of other cells changes.

There is a nice code snippet on hacking with swift which does exactly what you need. I use this snippet in my own projects too.

So in your case it would look like this:

    let backgroundView = UIView()
    backgroundView.backgroundColor = UIColor(red:0.94, green:0.94, blue:0.94, alpha:1.0)
    cell.selectedBackgroundView = backgroundView

Replace your code with this and I think it will be ok. Hope this helps.

EDIT:

To change only the color of the swiped cell you could try to insert an else statement after your if statement. You could try this:

let selectedCell = self.Nomination2TableView.cellForRow(at: indexPath) as! Nomination2TableViewCell

if filteredMessageIDforSwipe.count == 0  {

selectedCell.contentView.backgroundColor = UIColor(hexString: "#ffffff")
} else {
selectedCell.contentView.backgroundColor = UIColor.white 
//or whatever the color of the background is by default
}

Let me know if this doesn't solve it!

EDIT 2:

I recreated the core of your problem in an (overly simplified) little test app. So to recap, this is what I understood to be the core of your problem: you would like to set the color of the cells you swiped in a table view. You would also like them to stay that color when you reopen the app and also prevent other cells from getting colored because of cell reusing.

In the solution below I have not used the exact same objects and variables that you use since that would be difficult to recreate without your complete code. What this is instead is the logic that you could implement in your own solution. So it seems that you keep the selected cell indexPaths in filteredMessageIDforSwipe - so you should iterate over that array and set the color for the cells with the corresponding index paths. It's important to set the default cell background color before you call the for loop.

Here is my sample ViewController:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var tableView: UITableView!

let userDefaults = UserDefaults.standard

let testArray = ["Test1", "Test2", "Test3", "Test4", "Test5", "Test6", "Test7", "Test8", "Test9", "Test10", "Test11", "Test12", "Test13", "Test14", "Test15", "Test16", "Test17", "Test18", "Test19", "Test20"]

var selectedCellIndexPaths = [Int]()

override func viewDidLoad() {
    super.viewDidLoad()
    
    tableView.delegate = self
    tableView.dataSource = self
    if let values = userDefaults.value(forKey: "IndexPathsArray") as? Array<Int> {
    selectedCellIndexPaths = values
    }
    print("selected", selectedCellIndexPaths)
    
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return testArray.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    
    cell.textLabel?.text = testArray[indexPath.row]
    
    cell.backgroundColor = UIColor.white
    
    for indx in selectedCellIndexPaths {
        if indexPath.row == indx {
            cell.backgroundColor = UIColor.red
        }
    }
    
    return cell
}

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    
            let cell = tableView.cellForRow(at: indexPath)
    
    let action = UITableViewRowAction(style: .default, title: "Mark Read") { (action, indexPath) in
        
        cell?.backgroundColor = UIColor.red
        
        self.selectedCellIndexPaths.append(indexPath.row)
        
        self.userDefaults.set(self.selectedCellIndexPaths, forKey: "IndexPathsArray")
        print(self.selectedCellIndexPaths)
    }
    
    return [action]
    
}

}

Let me know if you need further help!

Upvotes: 1

zheck
zheck

Reputation: 298

When you scroll down, your tableview reuse previously loaded cells, keeping the background color, you need to reset the color to default in:

override func prepareForReuse() {
    // set the color to default
}

Upvotes: 0

Mahak Mittal
Mahak Mittal

Reputation: 131

Try this:

var selectedIndexPath: [IndexPath]?

 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath.append(indexPath)
tableView.reloadData()}

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.contentView.backgroundColor = selectedIndexPath?.contains(indexPath) ? UIColor(hexString: "#efefef") :  UIColor.clear}

Upvotes: 0

Related Questions