Iterate over all the UITableCells given a section id
Solution 1
To answer my own question: "how can I iterate over all the UITableCells
given a section id?":
To iterate over all the UITableCells of a section section
one must use two methods:
tableView.numberOfRowsInSection(section)
tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section))
So the iteration goes like this:
// Iterate over all the rows of a section
for (var row = 0; row < tableView.numberOfRowsInSection(section); row++) {
var cell:Cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section))?
// do something with the cell here.
}
At the end of my question, I also wrote a note: "Note: I want to set the AccesoryType to None for all of the cells in a section, programatically". Notice that this is a note, not the question.
I ended up doing that like this:
// Uncheck everything in section 'section'
for (var row = 0; row < tableView.numberOfRowsInSection(section); row++) {
tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: section))?.accessoryType = UITableViewCellAccessoryType.None
}
If there is a more elegant solution, go ahead and post it.
Note: My table uses static data.
Solution 2
You misunderstand how table views work. When you want to change the configuration of cells, you do not modify the cells directly. Instead, you change the data (model) for those cells, and then tell your table view to reload the changed cells.
This is fundamental, and if you are trying to do it another way, it won't work correctly.
You said "I need the array of cells before modifying them…" Same thing applies. You should not store state data in cells. As soon as a user makes a change to a cell you should collect the changes and save it to the model. Cells can scroll off-screen and their settings can be discarded at any time.
@LordZsolt was asking you to show your code because from the questions you're asking it's pretty clear you are going about things the wrong way.
EDIT:
If you are convinced that you need to iterate through the cells in a section then you can ask the table view for the number of rows in the target section, then you can loop from 0 to rows-1, asking the table view for each cell in turn using the UITableView cellForRowAtIndexPath
method (which is different than the similarly-named data source method.) That method will give you cells that are currently visible on the screen. You can then make changes to those cells.
Note that this will only give you the cells that are currently on-screen. If there are other cells in your target section that are currently not visible those cells don't currently exist, and if the user scrolls, some of those cells might be created. For this reason you will need to save some sort of state information to your model so that when you set up cells from the target section in your datasource tableView:cellForRowAtIndexPath:
method you can set them up correctly.
Solution 3
Im using same way of iterating all table view cells , but this code worked for only visible cells , so I'v just add one line allows iterating all table view cells wether visible they are or not
//get section of interest i.e: first section (0)
for (var row = 0; row < tableView.numberOfRowsInSection(0); row++)
{
var indexPath = NSIndexPath(forRow: row, inSection: 0)
println("row")
println(row)
//following line of code is for invisible cells
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: false)
//get cell for current row as my custom cell i.e :roomCell
var cell :roomCell = tableView.cellForRowAtIndexPath(indexPath) as roomCell
}
* the idea is to scroll tableview to every row I'm receiving in the loop so, in every turn my current row is visible ->all table view rows are now visible :D
Solution 4
For Swift 4 I have been using something along the lines of the following and it seems to work pretty well.
for section in 0...self.tableView.numberOfSections - 1 {
for row in 0...self.tableView.numberOfRows(inSection: section) - 1 {
let cell = self.tableView.cellForRow(at: NSIndexPath(row: row, section: section) as IndexPath)
print("Section: \(section) Row: \(row)")
}
}
Solution 5
Im using this way of iterating all table view cells , but this code worked for only visible cells , so I'v just add one line allows iterating all table view cells wether visible they are or not
//get section of interest i.e: first section (0)
for (var row = 0; row < tableView.numberOfRowsInSection(0); row++)
{
var indexPath = NSIndexPath(forRow: row, inSection: 0)
println("row")
println(row)
//following line of code is for invisible cells
tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: false)
//get cell for current row as my custom cell i.e :roomCell
var cell :roomCell = tableView.cellForRowAtIndexPath(indexPath) as roomCell
}
* the idea is to scroll tableview to every row I'm receiving in the loop so, in every turn my current row is visible ->all table view rows are now visible
Comments
-
sports almost 2 years
Using Swift, how can I iterate over all the
UITableCells
given a section id (eg: all cells in section 2)?I only see this method:
tableView.cellForRowAtIndexPath
, which returns 1 cell given the absolute index, so it doesn't help.Is there an elegant and easy way?
Note: I want to set the AccesoryType to None for all of the cells in a section, programatically, say: after a button is clicked, or after something happends (what happends is not relevant for the question)
I have the reference for the UITableView and the index of the section.
-
sports almost 9 yearsBut I need the array of cells before modifying them... before using realodSections.
-
Lord Zsolt almost 9 yearsWhy do you need them?
I want to set the AccesoryType to None for all of the cells in a section
- that you can do inside thecellForRowAtIndexPath
method. -
sports almost 9 yearsAnd what is the index for the cells in the given section?
-
Lord Zsolt almost 9 yearsExcuse me? I really don't understand what you're asking. Do you know how a table view works? Post your code to pastebin and provide a link, it might be helpful.
-
sports almost 9 yearsQuoting you: "that you can do inside the cellForRowAtIndexPath method". How?
-
sports almost 9 yearsThere is no need to post the code because the answer is pretty clear: How can I modify the AccesoryType of every cell under a given section? I dont want to touch the cells of other sections. You are not answering the question in your post.
-
Lord Zsolt almost 9 yearsI AM answering the question in the post, you just don't know how table views work.
-
sports almost 9 yearsYou can use a UITableView with static data or dynamic data. Also: your post doesnt answer the question.
-
sports almost 9 yearsA note for everyone: If someone asks how to use a "goto" in java, you dont answer: "you shouldnt use a goto in Java". Of course you can add that as a comment, but not as an answer. (By "comment" and "answer" I mean in the context of StackOverflow)
-
Duncan C almost 9 yearsYeah well, the answer is "don't do that." SO doesn't lend itself to "Your question doesn't make sense" type answers, which is what my answer is.
-
sports almost 9 yearsMy question is valid enough for itself ("as is"). Its a technical question.
-
Schemetrical almost 9 yearsThis does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post.
-
Schemetrical almost 9 yearsYou're right, OP needs to understand how
UITableView
s work. -
Nathan Tuggy almost 9 years@Schemetrical: Answers that explain why an approach is invalid or unwise, and that then explain at least somewhat of what to do instead, are perfectly legitimate. This is such an answer.
-
sports almost 9 years@NathanTuggy, this is not "such an answer", because Duncan C doesn't know the context. This is a technical question. You cant start throwing OPINIONS about "the approach" when the question is such as, I repeat: "how to iterate over all the UITableCells from a given section".
-
Duncan C almost 9 years@Sports, see my edit. I outlined what you need to do in order to get the on-screen cells for a given section.
-
AlexWoe89 over 8 yearsgreat, I searched over 2 hours, thank you very much!
-
Atef about 7 years:D :D :D Nice one xD
-
IsPha about 7 years@Abo3atef what really nice is that you didn't accept me after the technical interview :D :D
-
ToolmakerSteve about 7 yearsAh, so many people with misguided answers, promoting an approach that will seem to work, but fail sometimes on some devices - a testing nightmare. Here is the problem: if user has scrolled down the list of rows, at some point the first row will be discarded. If user makes a change on the currently visible row, the change might be lost when this code starts back at the first row - there is no guarantee as to when cells are kept once you start scrolling through a long table. Worse, might be different on different devices or iOS versions.
-
ToolmakerSteve about 7 years@sports - yes and no. It would have been far better if people started by asking some version of "what problem are you trying to solve, for which you think this is a useful technique". And then decided how to answer. However, in this specific case, the explanation of why you almost never should be attempting to do what you are asking, is too long to put into a comment. People are learning a technique that will seem to work fine - and then they'll hit a situation where it breaks, and have no idea why. You seem to know what you are doing; but the next person reading probably won't.
-
ToolmakerSteve about 7 years@sports - but Duncan C is correct: the Apple approved way to alter table cells, even for the simple case you describe is alter the model and then tell the table to update. Clearly that isn't the answer you are looking for, but it is considered one form of valid answer in Stack Overflow. Q & A's aren't just for the original person asking the question. The larger value is for future readers. Here, those readers need to be warned that this is not a road to go down, unless they are very sure.
-
ToolmakerSteve about 7 years(sorry, in case it isn't clear, I was commenting on your comment "If someone asks how to use a goto in java ...")
-
IsPha about 7 yearsHi @ToolmakerSteve, for the test case you're mentioning, it's easy to keep changed indexes via like dictionary . The idea is that when you scroll the cellforrowatindexpath gets called. So keep an eye on changed indexes and take care of them while reloading table due to scrolling. If your test case is like : You have a long list of ratings and you rate while scrolling, will provide code for this case that I faced a lot , otherwise you can give more details on your test case if it is technically different.Thanks for your reply.
-
Duncan C about 7 yearsTo finish out this discussion, my answer is not an opinion, it is well documented by Apple, and also borne out through painful experience. If you grab on-screen cells and change them without updating the data model to reflect the change, then when you scroll the table view and scroll back, the cells that you've changed manually lose their changes, and other cells that recycle your changed cells are likely to be set up incorrectly as well.
-
balazs630 almost 7 yearsWorks fine in Swift 3, too
-
WBuck over 6 yearsThis worked well for me. I wanted to enable/disable cells for specific headers and this allowed me to do that.
-
Duncan C about 6 yearsI edited my answer to give you the information you asked for, but the simple fact is that you can't iterate through all the cells in a section unless those cells all happen to be on-screen. If a cell is not currently on-screen then you'll get
nil
in response to the table view'scellForRow(at:)
method. -
Duncan C about 6 yearsQ: "How do I fly my airplane twice as far as it's maximum cruising range". A: "You can't. You will run out of fuel and have to make an emergency, unpowered landing long before you reach your destination."
-
Hattori Hanzō over 5 yearshelpful answer!
-
jbm over 5 yearsWait @ToolmakerSteve, is that actually true? Will the tableView/collectionView actually discard the cells? Obviously it doesn't update them/render them, for performance reasons, but it was not my understanding that it would actually "discard" them. This would suggest that, rather than just "visible" and "invisible" cells, there's actually a third state... That seems odd. The real point, I suppose, is that we shouldn't be iterating "all cells" if that's what we want; we should be iterating the dataSource (otherwise, we're treating the view as a model).
-
ToolmakerSteve over 5 years@IsPha - Bottom line: don't do what you are trying to do. It will eventually bite you. Its a misuse of the architectural design. Don't use the array of cells as information storage. For example, if the model array has 1000 rows, do you really want to iterate through 1000 cells, forcing them all to be created and then thrown away? Do you really want to manage additional information to keep track of which ones changed? You want to rely on iOS not discarding them as they go offscreen? Its much better to use as intended. Each cell should have a backing model, which changes as cell changes..
-
ToolmakerSteve over 5 years.. But the primary reason I am so strongly against the approach you use is that it cannot be guaranteed to be correct - its not testable. Here is how I got bit, that forever cured me of doing what you do: I had 4 cells. They were all visible. Worked great. I didn't bother with backing store. Then a change was made that grew all the cells by 1 text line. Still worked great. EXCEPT on one small phone, the containing rectangle got squeezed. One cell was now offscreen. Still seemed to work, except if you scrolled to bottom then back. Info lost. Obvious in hindsight, but was a PITA.
-
ToolmakerSteve over 5 years@mrwheet - there is no third state. What happens is that the cell object gets reused for a different source array index. It still exists, but it is showing different information!! As you say, the correct answer is to iterate the dataSource, not the view.
-
ToolmakerSteve over 5 yearsNote that if your page design includes a user option to "cancel" - to discard changes - this means you have to copy the model state on page entry, because you modify model state immediately on every UI change. When user "cancels" you keep the "before" copy. When user "accepts" you keep the changed copy. [Avoiding this extra coding is one reason it is so tempting to do what isPha proposes, and that I am strongly cautioning against. Unless you can guarantee cells are never reused nor discarded, nor even "recreated" by iOS from model; e.g. a phone call interrupts your app!]
-
Duncan C over 4 yearsThis is really, really bad advice. If you scroll through the table view, updating all the cells, either the scrolling won't do anything, or it will cause some of the cells you've updated to scroll off and get recycled. in the second case, those cells that scroll away will loose their custom configurations. To say "you can solve that by keeping a dictionary of updated indexes and "keeping an eye on" those cells is a hack upon a hack, and a truly awful idea. Just update your data model and tell the affected cells to redraw themselves!
-
Duncan C about 2 yearsNo, this isn't a nice one. This is a hack upon a hack to reinforce the wrong way of loading cells. You should not try to force a table view to load all of its cells. That is not how table views are designed to work, and is destined to fail at some point. This is objectively bad practice.