Can UICollectionViewUpdateItem be used to update a single label? - ios

I'm trying to update a single label in a collection view cell. I know how to update the entire collection view and how to update a single cell, but I want to update only a label because if the whole cell updates it will change other stuff which I don't want to change yet. In the docs I found something called UICollectionViewUpdateItem but cannot figure out how to use it. Is this capable of updating a single label or if not is there another way?

If you know the index path of the cell you want to update, you can get the cell using cellForItem(at: IndexPath). From there you can edit your cell's label.
However, I suggest you don't change your underlying data so that it's in a state that is unsuitable for presentation in the UI. The collection view can decide to create cells at times that are unpredictable to you.
UICollectionViewUpdateItem is used to indicate e.g. from where a cell moved, and where it moved to, or that a cell at a certain position was deleted, updated, etc. The docs state:
You do not create instances of this class directly.
So it means you should only create it indirectly, for example through reloadItems(at: [IndexPath]). It will reload your entire cell and should not be used to update a single detail of it.

Related

Using KVO to update table cells from model upon any change to model

I may have multiple table cells which are updated from the same model. I want to be able to update all of them when the model changes. So I assume I will use KVO to do that. My question is, how can I have each react to any change to the model, not just the part that each cell will display? i.e. if one item in the model changes, I want all of the cells to update themselves.
try to use tableView.reloadData() immediately after changing the model. On the other hand you may also try to use RxSwift and bind your model items to tableView - your tableview will then reload whenever the model changes. Refer to these articles as simple examples of RxSwift usage with tableView:
http://rx-marin.com/post/bind-multiple-cells/
https://daddycoding.com/2020/04/20/rxswift-uitableview-with-section/
So I assume I will use KVO to do that.
Table cells are very transient -- the cell for a given row in your table will get reused for a different row almost as soon as it scrolls off the screen. So long-term relationships really aren't something that cells are good at, and you probably don't want to go setting a cell up to observe part of your model directly. Instead, let the view controller worry about the model and reload any affected cells when the model changes. If you want to use key-value observing to let the view controller watch the model, that'll work fine.
My question is, how can I have each react to any change to the model, not just the part that each cell will display?
When the view controller learns that the model has changed, it should tell the table to reload its data. At that point, the table will go through the process of recreating the visible cells, and the effect will be that all the cells will appear to "update themselves."
Please try to reload table by using tableView.reloadData() after the data changed.

Getting the data of all TableView Cell which all has TextFields in it (including the hidden views)

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.

Is there a way to directly access UICollectionView elements without reloading?

I have another question open where I'm trying to figure out how to reload the collectionView without auto-scrolling. I was also realizing there are a lot of other situations where I will need to change things in the collection view. Also I have some items that I will want to change the .alpha on and change the text of. Is there a way to do all of this in Swift? For example (to be specific) if I have a collection view with a view in each cell and that view has a textField in it, can I change the alpha and text, (change alpha with animation even) without reloading entire table?
Look at the documentation for UICollectionView. There are several "reload" methods:
reloadData()
reloadSections(_:)
reloadItems(at:)
If you just want to reload a single item, update your data source's data and then call reloadItems(at:) passing in the index path for the item.
Another option, if a cell is currently visible, is to use the cellForItem(at:) method to get a reference to an existing cell. Then you can directly access UI components of the cell as needed. You should also update your data model as needed so if the user scrolls and comes back, the cell will be rendered properly.
Most appropriate where you can update your custom view of a particular UIcollectionViewcell is reloadItemsAtIndexPaths.
You would be handling a particular item than whole collectionview with reloadData.
You can handle it via notifications or some call backs in your code where you can make decision when to update which cell.
Hope it will help you.

Reload data in table view without harming animation

I have a UITableView-based in-game shop.
Every cell has a "BUY" button which is mostly enabled and can be switched to "BOUGHT" if the item is a one-time purchase or can be disabled if there are not enough money.
Right now what I do is calling reloadData every time buy button is being pressed in order to update visible cells and the current cell itself. (I have to update all cells, because after purchase it is possible that there wont be enough money for visible item cells).
But it causes weird animation glitches, like when I click on one cell's buy button and animation finishes on another one.
I think this happens due to reusability of cells. So what I want to know is how to reload data in the whole table view without harming native animation.
The only thing I can think of is not to use reusable cells and cache them all, but I dont think this is a good programming practice.
First, make sure that your view layer and model layer are separate. There should be some non-view object that knows about each item; we'll call it Item.
Now, create an ItemCell (you probably have one already). That's your reusable cell. Hand it the Item. It should configure itself based on the data in there.
Use KVO, delegation, or notifications to let the cell observe its item. When the Item changes its status, the cell should update its own button.
When your cell is reused, you'll pass a new item to it. It should stop observing the previous one, and start observing the new one (and of course reconfigure itself to match the current status).
By separating the views (which are reusable and transitory) from the model (which is stable and long-lived), you get the performance benefits of cell reuse with correct animations and no need to call reloadData.
You could have a reloadCell method in the cell class and loop through the table's visibleCells and update their UI that way. That way they are not recreated (or re-used), they just have their relevant UI pieces that could have changed due to the new data updated.

configure UICollectionView cell on initial lookup

I am getting cells for a UICollectionView by calling dequeueReusableCellWithReuseIdentifier:. I want to set some specific configuration information the first time my cell is returned from this method and not subsequently when it gets reused. Is there a hook somewhere where I can run "one time" code on collection view cells?
Obviously I could just set this information every time or use a boolean to keep track of whether or not the cell has been initialized, but I'd like to know if there's a cleaner way first.
This is easy enough to do from within a cell's implementation but there's no convenient way for a data source to differentiate newly created vs reused cells. If your configuration must be supplied by the data source then the data source probably need to check if the cell has been configured already.
The cells will be created once so you can use init or awakeFromNib to set some initial state. Cells will then have prepareForReuse called when being reused allowing you to perform any changes you need to make per-use.
The way I ended up solving this was to put my own view inside a generic UICollectionViewCell with a view tag. Then, when I go to deque my cell, I pull out the view using viewWithTag. If I get nil back, it's the first time this code has run, so I can init my view using my own constructor normally. This seemed slightly better than keeping track of a boolean in the cell implementation.

Resources