I have a VERY annoying problem.
I have a tableView with more or less 50 cells, displaying some options to which I can select the ones I want. I read in Apple's documentation that by default the cells are reused when they are not displayed. With this, if I select the first cell, every 6 cells 1 is marked, that is, if I select the first 6 cells, ALL cells in the table are marked!
My table view allows multiple selections. The selection is being made like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
}
How do I solve this? I know you have such a "prepareForReuse ()" for the subclass, would that be the solution? If so, can you give me an example of how you would do it?
here is code it may help you
var arr_selectedindePath = NSMutableArray()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if arr_selectedindePath .contains(indexPath) {
arr_selectedindePath .remove(indexPath)
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.none
}
else
{
arr_selectedindePath .add(indexPath)
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCellAccessoryType.checkmark
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell")!
if arr_selectedindePath .contains(indexPath) {
cell.accessoryType = .checkmark
}
else
{
cell.accessoryType = .none
}
}
You will need to update your data model accordingly so that when cellForRowAtIndexPath is called it displays cells with updated content.
If you don't use a datamodel you will need to store the indexPath's in a mutable array and make a check whether the current indexPath is marked or not.
Hope this helps.
In your custom cell
1.Create a delegate
protocol CellDelegate {
func didTapOnButton(_ cell:Cell)
}
2. Declare delegate
var delegate:CellDelegate?
3.Override this method
override func prepareForReuse() {
super.prepareForReuse()
self.delegate = nil
}
#IBAction func buttonTapped()
{
self.delegate!.didTapOnButton(self)
}
In your tableview controller
1.Implement delegate method
2.Inside cellForRowAtIndexPath assign tag value to cell.button
3.implement this method
func didTapOnButton(_ cell: Cell) {
print("off button clicked at index \(cell.button.tag)")
}
Related
I am using a UITableView and what I am doing is I am changing the color of the cell when I tap on the cell using didSelectRow function of UITableView at cellForRowAt. The thing which is bothering me is when I scroll down or scroll up, those cells whom I changed the color before were changed to other cells. Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCell(withIdentifier: "TasksTableViewCell") as! TasksTableViewCell
cell.backView.backgroundColor = .white
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = myTableView.cellForRow(at: indexPath) as! TasksTableViewCell
cell.backView.backgroundColor = UIColor(named: "primaryViewColor")
}
Does anyone knows why this happens? Does anyone has a solution that when only those cells changes color whom I tap on, and when I scroll down or move up only those cells have the other color?
cellForRowAt will be called every time that cell is displayed.
you need selected list to save selected index.
var listSelected: [Int] = []
and
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TasksTableViewCell") as! TasksTableViewCell
cell.backView.backgroundColor = listSelected.contains(indexPath.row) ? UIColor(named: "primaryViewColor") : .white
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if listSelected.contains(indexPath.row) {
listSelected = listSelected.filter{$0 != indexPath.row}
} else {
listSelected.append(indexPath.row)
}
tableView.reloadRows(at: [indexPath], with: .automatic)
}
I encountered do you see the problem many times. Even if using and iVar can solve the problem, You are mixing "Controller" logic and "Model" logic.
I usually prefer to move "selection" state inside the model.
Suppose You have a class "Contact" you use to fill cell data (usual MVC pattern)
I add:
class contact{
..
var selected = false
}
AND in TV delegation method I use to apply selection, OR better I use a custom selection method in a custom cell (for example to see a √ element in cell)
As a bonus multiple selection come for free, and you can also save current selections for next run :)
So as I understand you select a cell and after that other cells look like they are selected?
If so I think this is happening because you change the background color of the cell and tableViews and collectionViews are reusing the cells, basically keeping the background you changed behind.
TableViewCells are reused as soon as they leave the visible area.
This means that a cell whose background you have colored will be deleted from the view hierarchy as soon as it is scrolled up or down. If the corresponding row is scrolled in again, the function cellForRowAt is called again for this IndexPath and the cell gets a white background.
The easiest is to save the IndexPaths of the selected cells and check in the cellForRowAt function if the current cell has to be selected.
Add the following var to the viewController class:
var selectedIndexPaths = Set<IndexPath>()
and modify the tableView delegate methods:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = myTableView.dequeueReusableCell(withIdentifier: "TasksTableViewCell") as! TasksTableViewCell
cell.backView.backgroundColor = (selectedIndexPaths.contains(indexPath) ? UIColor(named: "primaryViewColor") : .white)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if selectedIndexPaths.contains(indexPath)
{
selectedIndexPaths.remove(indexPath)
}
else
{
selectedIndexPaths.insert(indexPath)
}
tableView.reloadRows(at: [indexPath], with: .none)
}
You can use
step 1: create model
class DemoModel {
var isSelected: Bool = false
var color: UIColor = .While
}
step 2: and in tableview
var listDemo: [DemoModel] = [DemoModel(),...]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCell(withIdentifier:
"TasksTableViewCell") as! TasksTableViewCell
var obj = listDemo[indexPath.row]
cell.backView.backgroundColor = obj.color
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var obj = listDemo[indexPath.row]
obj.color = UIColor(named: "primaryViewColor")
tableView.reloadRows(at: [indexPath], with: .automatic)
}
I've a table view controller in which I want to dynamically create a row (cell). How can I access row by index number in Swift? The following is what I did and now I'm stuck here.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "teamStats", for: indexPath) as! TeamStatsTableViewCell
return cell
}
This is my TableViewController code in which I want to check if the row is at a specific index number.
This method is a delegate method which will be called every time a cell is displayed on your UITableView. So if you want to do something with a cell in a particular index, you do the following. Please go through Apple docs and get a better understanding of what delegate methods are.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "teamStats", for: indexPath) as! TeamStatsTableViewCell
if indexPath.row == requiredIndex {
//Do something.
}
if indexPath.row == lastIndex { // you have the last index with you. replace this variable with that.
cell.firstlabel.text = ""
}
return cell
}
Please try this code:
if indexPath.row == 1{
//Your code
}
You can check the section :
if indexPath.section == 1{
}
It may helps to you.Thank you
So I have been looking all over the internet for an answer to this but to no avail. Basically, I have code to have cells in a tableview have a check mark when selected, which works fine. However, when selected, I want the indexPath to be saved in a user default so that the next time the user views it, that particular cell that she had selected before can be pre-selected:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
UserDefaults.standard.set(indexPath as IndexPath, forKey: "OnHomeShow")
}
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
}
}
The saving in the user default didn't work! Anyone able to help? Furthermore, how do I get it so that I can have a pre-selected cell? I used this code here:
override func viewDidAppear(_ animated: Bool) {
if let x = UserDefaults.standard.object(forKey: "OnHomeShow") as? IndexPath
{
//I want to pre-select the cell set by the user-default
}
else
{
//I want to pre-select the first cell
}
}
But I have no idea what code to put to select a cell programmatically! Anyone know how to help? Thanks!
Here is my code for the WillDisplayRow function:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let monetaryCell = tableView.cellForRow(at: [1,0])
//configure you cell here.
if AppState.sharedInstance.filterPaymentMonetaryIsOn {
monetaryCell?.accessoryType = .checkmark
return monetaryCell!
} else {
monetaryCell?.accessoryType = .none
return monetaryCell!
}
}
And in my DidSelectRow:
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
let monetaryCell = tableView.cellForRow(at: [1,0])
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
if indexPath == [0,0] {
print(indexPath)
} else if indexPath == [1,0] {
if AppState.sharedInstance.filterPaymentMonetaryIsOn == true {
monetaryCell!.accessoryType = .none
AppState.sharedInstance.filterPaymentMonetaryIsOn = false
} else {
monetaryCell?.accessoryType = .checkmark
AppState.sharedInstance.filterPaymentMonetaryIsOn = true
}
Can someone help me out as to why this isnt working? Also, I have alot more cells and when selected each one has the same properties as above. What I'm looking for is the checkmark to be displayed when I reload the view or close and reopen the view. I hope this makes sense. I'm sorry if this is a duplicate, I looked for a few hours but couldn't find a similar situation.
Thank you in advance.
Denis Angell
Move your check mark display functionality from cellForRowAtIndexPath method to willDisplayCell method of UITableViewDelegate. willDisplayCell method works in such a way that whenever the cell about to appear on screen willDisplayCell method calls and desired functionality executes as expected.
I have a UITableView with an UITableViewCell, and I want to select them by clicking on it and deselect them by another clicking on them.
First, I have tried it with
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
// code
}
But I have noticed that this function don't get performed when you click a second time on a cell; it gets performed when you click on another cell.
But I want that you can click on several cells which are checked when you click on them.
So I have made this code (simplified):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected")
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! LektionTableViewCell
if cell.accessoryType == .none {
print("Type = none")
cell.accessoryType = .checkmark
Lektion.insert(Dateien[indexPath.row], at: Lektion.count)
} else if cell.accessoryType == .checkmark {
print("Type = check")
cell.accessoryType = .none
Lektion.remove(at: indexPath.row)
}
}
But it doesn't work correctly:
When I click on a cell, the text automatically turn to "Label" (The text it has in the view builder)
When I click on a cell with a check, the check doesn't vanish and Xcode says "Type = none"; that is wrong.
Can anybody help me?
I can't comment so I add it here.
From your code we don't see where you store the data for populating your cells. So I don't know if this can help you but suppose you have an array with your objects, one approach would be to set a "isSelected" variable on your object on the didSelectRowAt indexPath method and then call tableview.reloadData(). Something like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myObjects[indexPath.row].isSelected = !myObjects[indexPath.row].isSelected
tableView.reloadData()
}
Then on the cellForRowAtIndexPath method, when you create your cells you could easily set the accessoryType to either .checkmark or .none depending on the flag of your object.
Like this:
cell.accessoryType = myObjects[indexPath.row].isSelected ? .checkMark : .none
Hope that helps.
Remove following code from your didSelect method, as it dequeReusableCell should not be used outside cellForRowAtIndexPath.
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! LektionTableViewCell
if cell.accessoryType == .none {
print("Type = none")
cell.accessoryType = .checkmark
Lektion.insert(Dateien[indexPath.row], at: Lektion.count)
} else if cell.accessoryType == .checkmark {
print("Type = check")
cell.accessoryType = .none
Lektion.remove(at: indexPath.row)
}
You can get cell instance using cellForRowAtIndexPath inside idSelectRow
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: IndexPath) as? LektionTableViewCell {
// Perform your operations here.....
}
}
Now, solution to your query, that for didSelect is not called upon click (unless you select another row). Allow multiple selection to your table view.
Note: Use a flag (boolean) variable in your table array to set selection status while your table is loading data.