2

I'm stuck with a very specific problem while using a Table View (XCode 9, Swift 4). What I want to do is, make an array named foodDetailInfoArray with text values of the foodName label in the table cells which have been selected manually by the user. Currently, while the .setSelected method works for changing the UI for a cell as I want, it isn't helping me record the foodName.text value properly. The problem is that the text values get recorded even while scrolling the table view and the array values get replaced as well. Below is the code and a sample of the printed output.

var foodDetailInfoArray: [String] = []

@IBOutlet var unselectedCell: UIView!
@IBOutlet var foodName: UILabel!
@IBOutlet var carbonValue: UILabel!

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    // Configure the view for the selected state
    if selected == true {
        self.unselectedCell.backgroundColor = UIColor.init(red: 4/255, green: 206/255, blue: 132/255, alpha: 1)
        self.foodName.textColor = UIColor.white
        self.carbonValue.textColor = UIColor.white
        foodDetailInfoArray.append(foodName.text!)
    } else {
        self.unselectedCell.backgroundColor = UIColor.clear
        self.foodName.textColor = UIColor.black
        self.carbonValue.textColor = UIColor.black
    }

    print(foodDetailInfoArray)
}

The print statement gives me this sort of result: (This is when the cells are not even selected and I'm just scrolling the table view.)

["pepper"]
["pasta"]
["pasta", "pepper"]
["pepper"]
["pepper", "pasta"]
["stir-fry"]
["stir-fry", "stir-fry"]
["vegetable"]
["vegetable", "vegetable"]

Whereas, what I ideally want would be (in the order of clicking the cell that contains given foodName):

["pasta"]
["pasta", "pepper"]
["pasta", "pepper", "tomato"]
["pasta", "pepper", "tomato", "stir-fry"]

and if a certain cell is deselected then the name has to be dropped, ie if tomato is deselected, then array would be

["pasta", "pepper", "stir-fry"]

... and so on

PS: I'm not a programmer by profession and altogether self taught recently, so please let me know if the question is unclear in any way.

kavyajha1004
  • 25
  • 1
  • 7

2 Answers2

5

I would try the delegate method didSelectRowAtIndexPath for tableViews. Have your view controller adopt the UITableViewDelegate protocol and implement the following.

Suppose you have a foods array, and a foodsSelected array that's initially empty.

let foods:[String] = ["Apples","Avocado","Bananas"]
var foodsSelected:[String] = []

Now whenever a cell is selected, this delegate method is called and add or remove the selected food from the foodsSelected array.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    //Check if the selected food is in the foodsSelect array
    if(!foodsSelected.contains(foods[indexPath.row])){
        //If it's not, append it to the array
        foodsSelected.append(foods[indexPath.row])
    }else{
        //If it is, remove it from the array. 
        //Note there are many ways to remove an element from an array; I decided to use filter. 
        foodsSelected = foodsSelected.filter({$0 != foods[indexPath.row]})
    }
    print(foodsSelected)
}

Here is the output when I select these items in order: Apples, Avocado,Bananas,Avocado

["Apples"]
["Apples", "Avocado"]
["Apples", "Avocado", "Bananas"]
["Apples", "Bananas"]
Miket25
  • 1,895
  • 3
  • 15
  • 29
  • Hi! Thanks for answering.. one small problem though I think. In the code for didSelectRowAt, you've clicked "Apples" the first time. Second time when you click "Apples", it is actually a didDeselectRowAt action which hasn't been specified here (and was specified by @ronatory in his answer.), and thus this code block does not append OR remove "Apples" from the array. And if you click "Apples" the third time, the resulting array will be empty (incorrectly so). So the didDeselectRowAt also has to be specified – kavyajha1004 Feb 17 '18 at 23:15
  • @kavyajha1004 You make a good point, but that not quite the case. `didSelectRowAt`, if say clicking `Apples` twice, will add and then remove `Apples` from the array. `didDeselectRowAt` was never called. `didDeselectRowAt` is only called after clicking a new row e.g. first `Apples` then `Bananas`, the `didDeselectRowAt` would recognize `Apples` as being deselected. – Miket25 Feb 17 '18 at 23:31
  • Hey.. ok, I did try the method, but the issue is as follows. If the table contains just two items - "vegetables" and "egg". First I click "vegetables", and the array adds that, I click it a second time and nothing happens, then I click it for the third time and get an empty array, even though on the UI "vegetables" is selected. If I then click "egg", it simply appends the value in the empty array and the "vegetables" isn't included. Don't know why it's giving me a different result if working for you – kavyajha1004 Feb 17 '18 at 23:46
  • Do you happen to have `tableView.allowsMultipleSelection = true`? If so, this will interfere. – Miket25 Feb 17 '18 at 23:56
  • Oh yes.. Because I do need the multiple selection and had read that it should be set to true! Just out of curiosity, why does that interfere? And also why does it work when the didDeselectRowAt method is included? – kavyajha1004 Feb 18 '18 at 04:15
  • I just tried your solution the after removing the tableView.allowsMultipleSelection = true? The problem described above still persists however – kavyajha1004 Feb 18 '18 at 04:29
5

I would handle the selection and deselection of the cell via the view controller, so you can also use your foodDetailInfoArray better. With the help of this answer you could do it like that way:

import UIKit

class ViewController: UITableViewController {

    // example data
    let names = [ "pepper", "pasta", "stir-fry", "vegetable"]

    var foodDetailInfoArray: [String] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        // allow multiselection
        tableView.allowsMultipleSelection = true
    }


    // MARK: UITableViewDataSource

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 4
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
        cell.textLabel?.text = names[indexPath.row]
        // Don't show highlighted state
        cell.selectionStyle = .none

        return cell
    }

    // MARK: UITableViewDelegate

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // also do your UI changing for the cell here for selecting

        // Add your food detail to the array
        foodDetailInfoArray.append(names[indexPath.row])
    }

    override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        // also do your UI changing for the cell here for deselecting

        // Remove your food detail from the array if it exists
        if let index = foodDetailInfoArray.index(of: names[indexPath.row]) {
            foodDetailInfoArray.remove(at: index)
        }
    }

}

Result

enter image description here

ronatory
  • 7,156
  • 4
  • 29
  • 49
  • Hi! This worked for me..incidentally, was trying out a similar thing. Thank you so much for making this clear! I do have a follow up question though - what causes the scroll to be interpreted as a selected cell in the .setSelected method? – kavyajha1004 Feb 17 '18 at 23:04
  • Yeah, glad that I could help you! Because of your follow up question, it seems to be that since iOS 7 via the `touchesBegan` event UITableView deselects selected cells and selects on `touchDown`, so depending where you did call `setSelected` in your initial solution also the state got set and unset multiple times, also because of reusing the cells. See also here https://stackoverflow.com/questions/13275405/uitableview-selected-cell-doesnt-stay-selected-when-scrolled and here https://stackoverflow.com/questions/28360919/my-table-view-reuse-the-selected-cells-when-scroll-in-swift – ronatory Feb 17 '18 at 23:24