I have a table view which populates from an array of strings. Using UIGestureRecognizer users can reorder the cells and save the new order.
When pressing Save I need the new order of the cells to save to the array. I have tried many different techniques without success.
How can I capture all the current cell texts in order?
Don't rely on the cell contents as your data source. When the user reorders the cells, update your backing array to match (or a copy of it if you need to be able to revert changes).
That way, when you want to save, everything is already in the correct order.
Related
In my App's UITableView, when user swipes to delete an item, the App deletes not only the selected item but also other related items in the data source, which may be shown or not shown on the screen at the time. My first thought was that I need to iterate all cells in the UITableView to get the cells for those data items and call UITableView.deleteRows() to delete them. But a quick search on SO showed that it's impossible to iterate all cells due to the way how UITableView works.
So I wonder what's the right way to synchronize UITableView with data source in my case? Is it OK to just iterate UITableView.visibleCells? But if I understand it correctly, UITableView prefetches (I mean it gets more items than it needs to show on the screen) for performance reason, so what if the data item I delete are among the items that are prefetched by UITableView but are not shown yet? Should I call UITableView.deleteRows() to delete them too? But how can I get those cells?
I'd like to get every data that is within all cells in one tableview which is quite a long list.
I'm looking for an approach on how to retrieve everything including those hidden in view, which I know the views are reused. I think some of you might have experienced this problem before, what are your approach on this?
I've tried
let cells = self.tableView.visibleCells
then looping into every cell and saving each data to an array but it is not effective in getting those that aren't part of the view or hidden. Is there a way to get over this?
In cellForRowAtIndexPath, YOU are telling the table what is in each cell. So why would you turn around and ask the table what's in each cell? If the user puts "Hello" in your first cell, then scrolls the table enough to push that first cell out of view, then when the user scrolls back to the top, YOU are the one telling it to put "Hello" back in that first cell. YOU own the data source, not the table.
You need a data source. That can be "empty" at first, maybe an array of empty strings if that's what you want (each index in the array could map to a table row for example). But then, as the user interacts with the text fields in the cells, you need to update that data source with the text they entered.
You should use that data source as your source for the cellForRowAtIndex method. That way you can handle populating the cells when they are requested by the table, and you also know all the data when the user is done.
Why not just update the model each time the user taps a key when editing a textfield? You could create a protocol for that cell subclass and make your view controller the delegate for each cell. As long as cells are guaranteed to stay on the screen while you're typing (you'll get some weird behaviors if not) the cell can send a message to the view controller or whatever you hook it up to telling it what new value to store. Then everything is already stored for you when you need the full list, and you don't have to interact with the tableview.
I have a UITableView which I populate by a list of objects I'm getting from a Realm database. What I want to do is to create sections and group items in the list by a property value in runtime.
All of the examples of grouping items in UITableView I see online are operating with it a prearranged dictionaries.
Is it possible to do?
You can set up your table view data source any way you want. You could write code that decides on the fly which items belong in which sections, but I would advise against it.
I would suggest setting up a method that takes your list of Realm objects as input, and builds an array of sections containing sub-arrays of the rows. Then your cellForRowAtIndexPath method can simply index into your model data like normal.
I want to loop through a TableView and extract the text from all the selected rows. I suppose I "could" create and maintain a special array that is updated every time a row is selected/deselected using the didSelect/didDeselectRowAtIndexPath methods. But creating a separate array seems like an extra step. Is there no way to let the TableView itself serve as the array and then simply loop through it and get the selected rows? What would the code look like? I'm new to Swift, so this might be a silly question.
Part of the problem is that cells are supposed to be reused, and when used this way it is not possible to loop through them all. You could get around this by using a unique reuse identifier for each cell, such as the indexPath itself or some underlying unique id in your model. Then, you could indeed loop through all cells and retrieve whatever state you desired from each.
You would, however, find your application crushed under the weight of too many cells being instantiated and kept in memory. If you don't have many cells you won't be killed, but try it with a big data set and your app will enjoy a very quick death.
It is far more efficient to store one array with a bunch of id's than a large number of memory-intensive UITableViewCells.
As mentioned in comments, you should work with underlying datasource, not the table itself.
For example if your table shows rows from Array, it is way more faster to retrieve strings directly from that array than creating UITableViewCells and get strings from them.
Get indices of selected rows using UITableView's property indexPathsForSelectedRows.
Query datasource for each row.
As has been said the tableview only handles displaying, your datasource is what powers the data shown if you think about it.
Plus as said before the tableview dequeues cells as they scroll on and off the screen.
The best way to achieve what you want is to add a property to your datasource for each element that will allow you to filter out the select properties easily.
How are you storing the state for each selected cell currently? As this is the same functionally you would use to be able to generate your selected text array.
I have a custom view that exists in a cell in a tableview. The view is called bulletRow and it is a series of bullets that can be filled in or emptied when a user taps on them. Each cell in my tableview contains some bulletRows and I need to save them when the user taps on them. I have considered using Core Data, but I don't need them to persist when the app is shut down, I only need it to exist when the user scrolls past the dequeueing point.
Here is my situation right now: The default state for bulletRows is to have 5 dots, all of them empty. When a user taps on them they become filled. If the user scrolls down however, they get reset back to being empty. How can I save the state of the bulletRows?
The bulletRows have a property called numberOfFilledCircles which can be set at anytime to change the amount of filled in circles. This is all done in Swift as well.
In general, you should use something, such as an array, to hold the state of your table. The cells in your table should reflect that state, and update that state when selected.
You might start with an array of integers in your table view controller, like this:
var numberOfFilledCircles = [Int]()
Use the number of items in your array to determine how many rows to display in your table, by returning numberOfFilledCircles.count from your numberOfRowsInSection method.
You can populate the array in viewDidLoad. If you're hardcoding the rows, you can repeat this statement for as many rows you want:
numberOfFilledCircles.append(0)
Each Int in the array holds the value representing how many circles are filled (initialize to 0).
In your cellForRowAtIndexPath, use the appropriate value from your array when constructing your cell. For example, if your cell had a UILabel called numberOfFilledCircles, you would do this:
cell.numberOfFilledCircles.text = String(numberOfFilledCircles[indexPath.row])
Finally, in your didSelectRowAtIndexPath, update the array with the number of circles you want filled in:
numberOfFilledCircles[indexPath.row] = //whatever you want
The issue here is that the UI is not the model.
When the ui elements are pressed, you should send an action to the underlying model to update its state, and when cells are dequeued you should restore the checkbox state from the appropriate model element.