I'm using a UITableView, with 3 sections ( Static Cells )
Currency
Language
Social
They have different number of rows:
Currency has 3 rows ( USD, EUR, JPY )
Language has 2 rows ( EN, JP )
Social has 3 rows ( Twitter, FB, Line )
Right now, I have by default set a checkmark at the first row of every section. However, I would like to allow the user to set their default settings and change the checkmark accordingly based on what they have set.
My question is then how do I set the checkmark for 3 different sections each with varying number of rows?
Do I need to set an cell identifier for each Section? Do I also need to create a UITableViewCell swift file for each Section?
If the checkmarks are set in response to tapping on the cell, just implement tableView(_:didSelectRowAtIndexPath:):
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let section = indexPath.section
let numberOfRows = tableView.numberOfRowsInSection(section)
for row in 0..<numberOfRows {
if let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section)) {
cell.accessoryType = row == indexPath.row ? .Checkmark : .None
}
}
// ... update the model ...
}
Otherwise, you can set identifiers for each cell in your storyboard (or outlets if you prefer, since the cells aren't reused), and then just set the checkmark programmatically. For example, using a delegate method:
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if let identifier = cell.reuseIdentifier {
switch identifier {
"USD Cell": cell.accessoryType = model.usdChecked ? .Checkmark : .None
"EUR Cell": cell.accessoryType = model.eurChecked ? .Checkmark : .None
//...
default: break
}
}
}
There shouldn't be a need to create a separate subclass for each section/cell.
Just a quick update for Swift 3:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = indexPath.section
let numberOfRows = tableView.numberOfRows(inSection: section)
for row in 0..<numberOfRows {
if let cell = tableView.cellForRow(at: IndexPath(row: row, section: section)) {
cell.accessoryType = row == indexPath.row ? .checkmark : .none
}
}
}
Related
i try to show a popup where the user can multiple select rows to filter data. To do so i created this popupViewcontroller:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRow(at: indexPath)?.accessoryType == UITableViewCell.AccessoryType.checkmark{
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCell.AccessoryType.none
checkedCategories.remove(at: indexPath.row)
}else{
tableView.cellForRow(at: indexPath)?.accessoryType = UITableViewCell.AccessoryType.checkmark
checkedCategories.append(allcategories[indexPath.row])
}
}
But the problem is if the user clicks on the first position, it shows also a checkmark at the last item. This error occurs only after scrolling.
Don't use a separate array to keep index paths. That's very bad practice.
Cells are reused. You have to make sure that every UI element is set to a defined state in cellForRow.
In your data model, preferable a struct or class add a property
var isSelected = false
In cellForRow set the checkmark according to the property
let item = datasource[indexPath.row]
cell.accessoryType = item.isSelected ? .checkmark : .none
In didSelectRowAt toggle isSelected and reload the row
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
datasource[indexPath.row].isSelected.toggle()
tableView.reloadRows(at: [indexPath], with: .none)
}
This because cells are dequeued , so inside cellForRowAt
cell.accessoryType = checkedCategories.contains(indexPath.row) ? .checkmark : .none
Another note that may cause a crash to remove use
checkedCategories.removeAll(where:{ $0 == indexPath.row })
When it comes to creating a dynamic type table view in a storyboard, it automatically fills up the table with rows.
How would i disable the red lined section (unused rows yet) of the image?
Use tableView(_:cellForRowAt:) method from UITableViewDataSource, like so
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellId", for: indexPath)
if indexPath.section == DISABLED_SECTION_INDEX {
cell.isUserInteractionEnabled = false
} else {
cell.isUserInteractionEnabled = true
}
}
Or without if statement
cell.isUserInteractionEnabled = indexPath.section != DISABLED_SECTION_INDEX
I have this piece of code:
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if let selected = self.lastSelected {
tableView.cellForRowAtIndexPath(selected)?.accessoryType = .None
}
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.Checkmark
self.lastSelected = indexPath
I allows me to place a checkmark on the cell that I select and removes the previous checkmark. The problem I am having is that if the list is large and the cell is out of view, the checkmark is not removed when a new cell is selected.
I have tried adding tableview.reloaddata() but that did nothing.
Thoughts?
cellForRowAtIndexPath returns nil for non-visible cells.
To get a radio button like effect, you need to do something like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
configureCell(cell, atIndexPath: indexPath)
return cell
}
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
if indexPath == selectedIndexPath {
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedIndexPath = indexPath
// Note indexPathsForVisibleRows may be nil depending on your app's content.
// zip just joins two arrays together, into a single array of tuples.
zip(tableView.indexPathsForVisibleRows!, tableView.visibleCells).forEach({ indexPath, cell in
configureCell(cell, atIndexPath: indexPath)
})
}
I'm trying to show checkmark on tableview cell, but the checkmark appears only sometimes and it disappears when I scroll.
Below the code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("vvxxx12", forIndexPath: indexPath)
// Configure the cell...
cell.textLabel?.text = self.dataArray[indexPath.row] as? String //in dataArray values are stored
if dataArray.containsObject(indexPath)
{
cell.accessoryType = .Checkmark
}
else {
cell.accessoryType = .None
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark {
cell.accessoryType = .None
} else {
cell.accessoryType = .Checkmark
}
}
}
Just do following changes in your code to maintain checkmark into tableview while you are scrolling tableview
Result :
Its work fine now, Any problem let me know i will definitely help you to out.Enjoy..
For Swift 3 following worked for me to
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
yourtableView.cellForRow(at: indexPath as IndexPath)?.accessoryType = .checkmark
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
yourtableView.cellForRow(at: indexPath as IndexPath)?.accessoryType = .none
}
cell.textLabel?.text = self.dataArray[indexPath.row] as? String
This suggests that dataArray contains strings.
if dataArray.containsObject(indexPath)
This suggests that dataArray contains index paths. Both of these should not be true. One array for data makes sense, and another one for selected rows or rows to be checked also makes sense, but not the same array for both.
What's probably happening is:
Row selected - cell is then updated to have the checkmark accessory
Table scrolled and cellForRowAtIndexPath is called - at no point will dataArray contain an index path, so the accessory is always cleared.
You need to be updating your model when a row is selected, to store the index path of the selected row, or update a selected flag on your model objects.
I have 2 sections in my UITableView.
I want the first section to allow multiple cell selection and the second section to allow only single selection.
I tried some code but didn't work very well.
Code in swift if possible. Thank you.
You can simply try this. This solution works for me perfectly. Give it a try maybe worked for others...
Swift-4
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
else {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if indexPath.section == 1 {
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
cell.accessoryType = .none
}
}
}
Perhaps you could implement the table view's delegate methods:
tableView(_:shouldHighlightRowAtIndexPath:)
and
tableView(_:didSelectRowAtIndexPath:)
...and determine (from indexPath.row and indexPath.section) if the relevant section supports single/multiple selection (this will depend on your data model's custom logic -e.g.: "Section 0 supports multiple selection but section 1 does not"), and if it only supports single selection, check whether there is already a row selected (by accessing tableView.indexPathsForSelectedRows).
If there is a selected row already, you can:
Return false from tableView(_:shouldHighlightRowAtIndexPath:), and
Do nothing (just return) from tableView(_:didSelectRowAtIndexPath:) (I'm not sure if this method is actually called when you return false from shouldHighlight..., so perhaps check it).
This is easily achievable in two lines as follows: (Swift 4)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if sectionAllowsMultipleSelection {
if let indexPathsInSection = tableView.indexPathsForSelectedRows?.filter ({ $0.section == indexPath.section && $0.row != indexPath.row }) {
for selectedPath in indexPathsInSection {
tableView.deselectRow(at: selectedPath, animated: false)
}
}
}
}
If you want the selected row in section 2 to be the new selected row, this might work for you. Else, go with #NicolasMiari's answer.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 1 {
for i in 0..tableView.numberOfRowsInSection(indexPath.section) - 1 {
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: i, inSection: indexPath.section))!
if (i == indexPath.row) {
cell.accessoryType = .Checkmark
cell.selected = false
}
else {
cell.accessoryType = .None
}
}
}
else {
//Do whatever for the first section
}
}
Not very elegant, but hopefully it will give you an idea.