CardDeath
CardDeath

Reputation: 132

Small UITableView, remove dequeReuseableCell

I've read around so don't knock this as a duplicate unless my searching has been completely awful.

Basically I have a small table (5 Fixed rows) however they do have indexes associated with them in a tuple (this is why I'm using the dynamic way rather than a static way.

The players goal is to swap the cells around. I am not using a nib to get these cells populated, this is how.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellID = "Cell"
    let customCell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! DefinitionMatchTableViewCell

    customCell.background.layer.borderWidth = CGFloat(2.0)

    if tableView == nameTableLeft{
        customCell.textToDislay!.text = nameList[indexPath.row].0
        customCell.background.layer.borderColor = UIColor(red: 255/255, green: 192/255, blue: 1/255, alpha: 1.0).cgColor
        let cellIDFromTuple = nameList[indexPath.row].1
       customCell.tag = cellIDFromTuple
    }

    if tableView == definitionTableRight{
        customCell.textToDislay!.text = definitionList[indexPath.row].0
        customCell.background.layer.borderColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.2).cgColor
        let cellIDFromTupleForDefinition = definitionList[indexPath.row].1
        customCell.tag = cellIDFromTupleForDefinition
    }

    customCell.background.backgroundColor = UIColor.clear
    customCell.backgroundColor = UIColor.clear
    return customCell
}

Now the problem is that on an iPhone SE the 5 cells can't fit which results in the top or bottom cell losing it's reference ID when checking if the values match. This makes the game impossible to complete, I need a way so I can populate the table with an index value associated with each string but so the table doesn't reuse the cell but it remains in memory.

The only method I can think of is writing a static table with the data but then providing each cell with an invisible index that it carries associated with the string. However I'd much rather do it dynamically.

Any advice on to keep the state of these cells would be greatly appreciated. Thanks!

EDIT 1

Here is the way I check the answers based on my index values in the table. I imagine the problem comes from .indexPathsForVisibleRows as some aren't visible

    for index in nameTableLeft.indexPathsForVisibleRows!{
            let leftCell = nameTableLeft.cellForRow(at: index) as? DefinitionMatchTableViewCell
            let rightCell = definitionTableRight.cellForRow(at: index) as? DefinitionMatchTableViewCell

if nameTableLeft.cellForRow(at: index)?.tag == definitionTableRight.cellForRow(at: index)?.tag{
                leftCell?.background.layer.borderColor = UIColor.green.cgColor
                rightCell?.background.layer.borderColor = UIColor.green.cgColor
                counter += 1
            }else{
                leftCell?.background.layer.borderColor = UIColor.red.cgColor
                rightCell?.background.layer.borderColor = UIColor.red.cgColor
            }
        }

As you can see I then check the values match up on the tags. However I'm struggling with this concept if I used something Different.

My Model

var nameList : [(String, Int)] = [("Trojan", 0),
                                  ("Virus", 1),
                                  ("Spyware", 2),
                                  ("Ransomware", 3),
                                  ("Leakware", 4)]
var definitionList : [(String, Int)] = [("Attaches onto another program. Replicates itself. Something loaded onto your device without your knowledge. Continues to spread", 0),
                                        ("When malware is disguised as something else. Usually an e-mail attachment", 1),
                                        ("Tracks a user's actions, this includes login details, internet habits and any secure accounts", 2),
                                        ("Locks a device or encrypts files and demands money to unlock it", 3),
                                        ("Threatens to distribute your information", 4)]

Edit 2

Thanks to some help in the answers I had decided to add this to my answer checking.

 for answerIndex in 0...4{
            if definitionList[answerIndex].1 == nameList[answerIndex].1{
                print("MATCH")
                counter += 1
            }else{
                print("NO MATCH")
            }
        }

I jumble both the definition and name lists to start with. I then decide to look at both of the indexes placed in the tuple based on the tables total length. Unfortunately I had to resort to magic numbers are you cannot iterate through a tuple.

So this little loop decides if the indexes line up in my tuples and if they do it's correct otherwise it's not. Thanks again everyone for the help.

Upvotes: 1

Views: 141

Answers (2)

KrishnaCA
KrishnaCA

Reputation: 5695

I also solved a similar problem. My advise is to keep reusing the cells. Store the data corresponding to each cell externally in a separate model class. Then store it in a mutable array. When you shuffle the cells, shuffle the data in the mutable array.

It can be done in the following way.

The model class

class model: NSObject {

   var name: String = ""
   var definition: String = ""
   var type: String = ""

   required init(name: String, definition: String, type: String) {
       super.init()

       self.name = name
       self.definition = definition
       self.type = type
   }
}

Custom UITableViewCell - trivial example for different layouts

class CustomTableViewCell: UITableViewCell {

    var nameLabel: UILabel = UILabel()
    var definitionLabel: UILabel = UILabel()

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

       nameLabel.textColor = UIColor.black
       nameLabel.font = UIFont.systemFont(ofSize: 15)
       nameLabel.textAlignment = NSTextAlignment.left
       self.contentView.addSubview(nameLabel)

       definitionLabel.textColor = UIColor.brown
       definitionLabel.font = UIFont.systemFont(ofSize: 15)
       definitionLabel.textAlignment = NSTextAlignment.right
       self.contentView.addSubview(definitionLabel)
    } 

    required init?(coder aDecoder: NSCoder) {
       fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
       super.layoutSubviews()

       nameLabel.frame = CGRect(x: 10, y: 10, width:   self.contentView.frame.size.width - 20, height: self.contentView.frame.size.height - 20)
       definitionLabel.frame = CGRect(x: 10, y: 10, width: self.contentView.frame.size.width - 20, height: self.contentView.frame.size.height - 20)
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

    }

}

Registering the cell:

self.tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "reuseIdentifier")

Using the CustomTableViewCell according to model:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)

    // Configure the cell...
    if model.type == "name" {
        cell.nameLabel.text = model.name
        cell.definitionLabel.isHidden = true
    }else if model.type == "definition" {
        cell.definitionLabel.text = model.definition
        cell.nameLabel.isHidden = true
    }

    return cell
}

The moving cells part:

let mutableArray:[model] = [model1, model2, model3, model4, model5] // model class holds data for each cell

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
     let source = sourceIndexPath.row;
     let destination = destinationIndexPath.row;

     let sourceModel = mutableArray[source]
     let destinationModel = mutableArray[destination]

     mutableArray[source] = destinationModel
     mutableArray[destination] = sourceModel
}

Feel free to comment if you have any doubts regarding this implementation. Feel free to suggest edits to make this better :)

Edit:-

Even if it's a tuple with it's index position, you can change the values when the rows moved accordingly.

var nameList : [(String, Int)] = [("Trojan", 0),
                                  ("Virus", 1),
                                  ("Spyware", 2),
                                  ("Ransomware", 3),
                                  ("Leakware", 4)]

let source = sourceIndexPath.row
let destination = destinationIndexPath.row

var sourceTuple: (String, Int) = nameList[source]
var destinationTuple: (String, Int) = nameList[destination]

sourceTuple.1 = destination
destinationTuple.1 = source

nameList[destination] = sourceTuple
nameList[source] = destinationTuple

Upvotes: 3

Jon Rose
Jon Rose

Reputation: 8563

First of all you should expect cells to be reused and not store state in your cells. But if you want a quick answer then set a different reuse identifier for every cell.

Upvotes: 2

Related Questions