Iterate over all the UITableCells given a section id

17,396

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

Share:
17,396
sports
Author by

sports

Sports! https://www.youtube.com/watch?v=l7u9hP4r1S8

Updated on June 13, 2022

Comments

  • sports
    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
    sports almost 9 years
    But I need the array of cells before modifying them... before using realodSections.
  • Lord Zsolt
    Lord Zsolt almost 9 years
    Why 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 the cellForRowAtIndexPath method.
  • sports
    sports almost 9 years
    And what is the index for the cells in the given section?
  • Lord Zsolt
    Lord Zsolt almost 9 years
    Excuse 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
    sports almost 9 years
    Quoting you: "that you can do inside the cellForRowAtIndexPath method". How?
  • sports
    sports almost 9 years
    There 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
    Lord Zsolt almost 9 years
    I AM answering the question in the post, you just don't know how table views work.
  • sports
    sports almost 9 years
    You can use a UITableView with static data or dynamic data. Also: your post doesnt answer the question.
  • sports
    sports almost 9 years
    A 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
    Duncan C almost 9 years
    Yeah 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
    sports almost 9 years
    My question is valid enough for itself ("as is"). Its a technical question.
  • Schemetrical
    Schemetrical almost 9 years
    This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post.
  • Schemetrical
    Schemetrical almost 9 years
    You're right, OP needs to understand how UITableViews work.
  • Nathan Tuggy
    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
    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
    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
    AlexWoe89 over 8 years
    great, I searched over 2 hours, thank you very much!
  • Atef
    Atef about 7 years
    :D :D :D Nice one xD
  • IsPha
    IsPha about 7 years
    @Abo3atef what really nice is that you didn't accept me after the technical interview :D :D
  • ToolmakerSteve
    ToolmakerSteve about 7 years
    Ah, 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
    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
    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
    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
    IsPha about 7 years
    Hi @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
    Duncan C about 7 years
    To 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
    balazs630 almost 7 years
    Works fine in Swift 3, too
  • WBuck
    WBuck over 6 years
    This worked well for me. I wanted to enable/disable cells for specific headers and this allowed me to do that.
  • Duncan C
    Duncan C about 6 years
    I 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's cellForRow(at:) method.
  • Duncan C
    Duncan C about 6 years
    Q: "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ō
    Hattori Hanzō over 5 years
    helpful answer!
  • jbm
    jbm over 5 years
    Wait @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
    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
    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
    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
    ToolmakerSteve over 5 years
    Note 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
    Duncan C over 4 years
    This 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
    Duncan C about 2 years
    No, 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.