Reputation: 132
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
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
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