Cancel animations in UITableView while scrolling - ios

I am a bit confused and don't have much else to turn to. I have an iOS application that gets messages from a server and populates it into a cell in a UITableView. Every time a certain object gets updated from the server that update triggers a cell animation. In our case we have a table of scores in a game. If someone loses a point their cell would flash red and if they gain a point their cell would flash green. Simple concept that works well and our users like the feedback. What they don't like is how when they scroll they see the cells have random colors associated with it and stay there for about a second. I believe that it has something to do with the way that the cells are being recycled. I tried various checks regarding this issue, checking that the cell has the same value, setting the color manually to white, etc. and non of these methods work.
All in all I am trying to only show the animation when the cell has been in view and not when the users is scrolling through the UITableView. Anyone have any advice or ideas?
Thanks

What is happening
Since table views reuse their cells you are ending up reused cells that is being flashed even though they shouldn't.
How to solve it
You can restore the reused cell to its default state either in preprareForReuse: on the custom table view cell or in the tableView:willDisplayCell:forRowAtIndexPath: delegate method (as ACB mentioned).
Since you are animating with UIView animations, you only need to change the value of the animated property to cancel the animation and return to the default state.
If you would have user Core Animation for the flash
If the animation was done using Core Animation you would instead have called removeAllAnimations on the animating layer. (For a flashing animation you probably wouldn't change the property on the layer, instead you would tell the animation to autoreverse. This would mean that removing the animation would change the color back to its original value.)

One option is to remove the animation and reset the color in willDisplayCell delegate method to the default color. If that doesn't work you can set the dequeueReusableCellWithIdentifier as follows,
NSString *aCellIdentifier = [NSString stringWithFormat:#"%d",indexPath.row];
UITableViewCell *aCell = [tableView dequeueReusableCellWithIdentifier:aCellIdentifier];
But this will be a problem if you have a lot data to display as it will create memory issues. Note that in this case you will not be reusing the table cells.

Related

How to determine whether the user has scrolled past the first cell?

On iOS, when you append something to the beginning of UITableView's data source, the cell will "animate in" and push the rest of the cells down only if the user is already scrolled to the top. When the user is scrolled down farther down the list and a new item gets appended to the top, iOS does not automatically move the cells down.
When a new cell comes in from the top, how do I determine between the two?
My use case is that when the user has scrolled down past the iOS point, I can display a "new item above" button for the user to click . (similar to Twitter)
I'm using dynamic height for cells, so hard coding the scrollView by pixel isn't the most ideal way. I'd like to utilize iOS's way to determine it.
There is an array available of all visible cells' index paths called indexPathsForVisibleRows. You can access that property and check to see whether or not your first row is one of them, and go from there. You can also access visibleCells property to get the actual UITableViewCells themselves.
How to determine whether the user has scrolled past the first cell: a UITableView is a subclass of UIScrollView, so you can just look at the contentOffset property.
I'm not exactly sure that that's enough to accomplish what you want though. You could also maybe take advantage of the fact that tableView's cellForRowAtIndexPath: method only returns a cell if it's visible, and request the first cell. I've never tried implementing something like this before though so there might be a better approach.

deleteRowsAtIndexPaths resets custom cells

I'm relatively new to Swift and iOS and I have one issue - I have a custom SwipeCell class that extends UITableViewCell. It's a cell that can be swiped left and right, and has buttons on each side. For some reason if I made the buttons out of the frame of the cell when user swipes the cell they would appear but could not call an action, so my solution was to make cell wider than it is (so buttons can fit in it). Because it was done this way my cell has to have an offset by default for the total width of the buttons of the left (let's say 100), so it's position is X:-100.
And that's fine, and everything works fines with the cells, however there is one huge issue - if I call the deletion of any cell from the tableView like this
tableView.deleteRowsAtIndexPaths([tableView.indexPathForCell(activeCell)!], withRowAnimation: .None)
tableView then deletes the cell, and all of the other cells that are currently visible (doesn't happen with cells above or below the screen bounds) get moved to X:0 instead of staying at X:-100, so I assume that deleteRowsAtIndexPaths calls some function that resets the visible cells positions to 0,0. I'm currently setting swipe cells positions with layoutSubviews() since the number of buttons is dynamic and couldn't be determined upfront, but layoutSubviews is not the function where the bug happens.
So to sum up question - what function does deleteRowsAtIndexPaths call after deleting a cell that resets/redraws the visible cells?
deleteRowsAtIndexPaths deletes a row(s) from the tableView. This is handled internally by iOS. From Apple documentation:
Deletes the rows specified by an array of index paths, with an option
to animate the deletion.
Another interesting thing to note here is that this method does not modify your model (object that holds data used by your table view cells to render on themselves). You have to do that yourself. The cells are deleted, but if you call reloadData without deleting the row from your Model, cell will reappear.
Expect that cells get deleted and created all the time. Cells are very, very temporary objects. Write the code to create one when needed, and don't make any assumptions. Don't assume the cell is there later, don't assume it's in the same row, don't assume it displays the same data (because cells are recycled).
Since I could find out what happens inside of deleteRowAtIndexPaths, and why it changes frames of each Cell, I decided to just do my own function that deletes a row, by obtaining cells with tableView.visibleCells, and then just moving cells bellow the deleted cell up by a height of deleted cell. Thank you all for trying to help me, and especially thanks to Abhinav who told me that it was handled internally, which help me decide to write a custom code for the deletion.

How do I perform cell selection animations while using reloadData?

I have a UICollectionView, and I override setSelected in the UICollectionViewCell subclass to perform a little bounce animation when the user selects the cell.
The problem is that on selection of the cell, the very selection of the cell alters the data for the other cells (for instance, imagine the other cells have a label in each one that has the amount of cells selected displayed). So I call collectionView.reloadData() so that all the other cells update as well to show the new data (for instance, the amount of cells selected in a label).
However, this reloadData() call seems to reset the UICollectionView completely. Any animations taking place are stopped and the cells are simply updated without animation.
How do I have the cell selection animation, and update the other cells at the same time?
I've tried calling reloadData() only after the animation has completed, but this makes it look like there's lag/delay in updating the other cells (as it doesn't update them until a moment after when the animation finishes), which doesn't look very good.
Similarly, I've tried calling reloadItemsAtIndexPaths: (and exclude the selected cell that will animate), but for whatever reason with collection views this method seems really slow. Like there's a noticeable amount of lag after pressing it.
What should I be doing here? What's the standard for reloading data without the collection view destroying in-progress animations?
Reloading throws away the existing cells and creates (or reuses) new ones. So any animation is inherently destroyed. So, don't reload.
Instead, update your data model as usual and then get the currently visible index paths and associated cells and directly update those cells.

Disable cell reuse on UICollectionView

I am using a UICollectionView in my app with gesture recognizers on the individual cells which allow the user to "slide open" the cell to reveal more data underneath.
The problem is, I am reloading the data in the CollectionView very often; as the app receives updates once every 3 seconds or so. This results in unwanted behavior with the collectionview cells being reused while a cell is in the process of being slid.
The user will start to slide a cell, the app will receive an update, reloadData, and a different cell will start receiving the gesture instead, and begin sliding.
I have tried disabling the app's updates while the slide is occurring, but that caused other complications within the app, so I am wondering if there is a way to disable the cell reuse, (I will only have 20 cells max, so I don't think there would be a large drop in performance).
Thank you!
Why don't you use a flag like needsReload and set it, if new data is available. After a slide you check for that flag and reload the collectionView, if needed? Is this not working?
If you don't want cell reusing, just use a default scrollView and put all your views in it!?
Disabling reuse is simple. Just don't use the dequeueReusableCell method.
Instead just alloc, init your cells. I would be careful of the performance and memory implications of doing so though...

UITableView reloadRowsAtIndexPaths hides row

I use UITableView with static cells.
If I use reloadData, than everything is OK.
If I try reloadRowsAtIndexPaths it hides row. Row appears if I drag tableView up-down(when cell is updated).
If your table cells are static (i.e., you are using the same cell object to replace the one that's currently displayed), what you're seeing is an artifact of the transition animation.
Think of it this way. Lets say you set UITableViewAnimationOptionFade. When the cell is to be replaced, the cell it's to be replaced with (which in this case is the exact same object) has a fade-in animation added to it. Then, the cell that is being replaced (again, it's all the same object) has a fade-out animation added to it. At the end, the cell is actually there, but it's invisible because the fade-out animation has made that cell object invisible.
In a non-static tableview, one in which the replacement cell is a different object than the cell to be replaced, this isn't a problem, as the animations are added to two different objects.
I had the same problem, it seems to be a bug. I experimented some and the problem doesn't occur when I set the animation option to UITableViewRowAnimationNone. Some other interesting stuff happens when you set that option to UITableViewRowAnimationTop, too.
I solved this by removing animation
tableView.reloadRows(at: [yourIndexPath], with: UITableView.RowAnimation.none)
Hope this helps to anyone searching for this.

Resources