UITableViewCell not clickable when Blinking animation is implemented on it - ios

No action is performed on UITableViewCell when blinking animation is implemented, it starts blinking that is fine but not clickable or swipable. I have tried some solutions but unable to fix it. Code is below.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.backgroundColor = UIColor.colorWithHexString(hexStr: "#FDFAEB")
cell.blink()
return cell
}
Animation extension code:
extension UIView {
func blink() {
self.alpha = 0.5;
UIView.animate(withDuration: 0.7, //Time duration you want,
delay: 0.0,
options: [.curveEaseInOut, .autoreverse, .repeat],
animations: { [weak self] in self?.alpha = 1.0 },
completion: { [weak self] _ in self?.alpha = 0.8 })
}
}
Please tell me, what is the reason, and how can I fix this?

By default user interaction is disabled during animations. you need to add the .allowUserInteraction option to your set of options.
(And note that if you're animating a view's position the user won't be able to tap on the view as it moves. Behind the scenes, a view jumps to it's destination position as soon as the animation begins. It only appears to move across the screen. If you want to handle tapping on a view as it moves across the screen it's a lot more complicated.)

Related

Slot machine animation using UITableView

I need to implement slot-machine animation according to provided design and timings.
It should perform infinite scroll, until some event will be triggered. After that animation, it should slow down and stop on defined position
For this task I have used next solution:
UITableView with fixed-height cell. It is the same cell with the only difference - icon or text (depends on indexPath.row)
Scroll is only down-to-up that's why I'm using last cell as start point in resetScrollPosition method
If first element reached, scroll position resets to start point
Animation performed as contentOffset change with linear option. In completion block, if animation is still needed, it's called again. If don't needed - slowing animation with easeOut option started
var isRolling: Bool = false
func startScroll() {
isRolling = true
UIView.animate(
withDuration: 0.05,
delay: 0,
options: .curveLinear,
animations: {
self.tableView.contentOffset.y -= self.rowHeight
},
completion: { _ in
if self.isRolling {
self.startScroll()
} else {
self.resetScrollPosition
UIView.animate(
withDuration: 0.7,
delay: 0,
options: .curveEaseOut,
animations: {
self.tableView.contentOffset.y -= 8 * self.rowHeight
},
completion: nil
)
}
})
}
private func resetScrollPosition() {
tableView.reloadData()
tableView.contentOffset.y = startOffset
tableView.reloadData()
}
func stopScroll() {
isRolling = false
}
The problems:
After calling resetScrollPosition, in animations completion block, tableviews contentOffset.y value is updated but tableView stays on the same position. I have tried to change direct contentOffset changing to setContentOffset, scrollToRow, scrollToRect, wrap it in main queue - no changes
Slowing animation should scroll 8 items. It's performed but first 6 items aren't visible during animation, only the last two.
Check the issue gif (jump 2 -> 11 is ok):
Replaced UITableView with UIScrollView
Uploaded code to gist - https://gist.github.com/OlesenkoViktor/76845c5448b421ead0a2303af2b1161d
Thanks #Paulw11 for his idea

UICollectionView cell disappears when i animate(with my custom animation) scrollToItem function

I just trying to make a custom animation when i scroll to item programmatically. So when I do not compose my animation and using default by cell do not vanishing, but when i put scrolltoItem func inside UIView.animate func the last cell first vanishing and then scrollToItem animates.
In the second picture in the uppermost collectionView the located before indie game cell firstly disappears and only then collectionView scrolls from indie game cell to the next
Why this behavior takes place? Why when i do not animating it purposefully in my way, and just calling scrollToItem with animated = true func, nothing eliminates? if someone do know what happens with the cells, please give me a clue.
UIView.animate(withDuration: 10, delay: 0, options: .curveEaseInOut, animations: {
self.appsCollectionView.scrollToItem(at: IndexPath(item: self.actualNumberOfTheCell, section: 0), at: .centeredHorizontally, animated: false)
}, completion: nil)
I think you should have to do it with self.view.layoutIfNeeded()
self.appsCollectionView.scrollToItem(at: IndexPath(item: self.actualNumberOfTheCell, section: 0), at: .centeredHorizontally, animated: false)
UIView.animate(withDuration: 10, delay: 0, options: .curveEaseInOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
Hope it will work
Your reused cells might be overlapping because of the sudden scroll. The latest cell might have not been dequeued properly and is perhaps initialized using a content of a previous cell -> which is most likely an empty cell that hasn't been properly displayed during the scroll.
Try to reset your cell content using:
prepareForReuse() inside your cell's controller.
You should reset your UI to a default state here.
Here's the docs link to it
Don't forget to call a UI display/setup method for your cell, which should display your UI elements.
Hope it helps!
I think the issue is caused by collectionView recycling the cell too early, as you are using scrollToItem with animation = false.
I tried to split the animation to several smaller steps, It works, but the scroll is not smooth. Here is the code:
// self is extended from UICollectionView
scrollTo(x: 100, duration: 0.6, count: 4) { (finished) in
}
private func scrollTo(x:CGFloat, duration:TimeInterval, count:Int, completion:#escaping (Bool)->Void)
{
let xOffset = (x - contentOffset.x)/CGFloat(count)
let durationPart = duration/TimeInterval(count)
scrollToPart(xOffset: xOffset, duration: durationPart, count: count, completion: completion)
}
private func scrollToPart(xOffset:CGFloat, duration:TimeInterval, count:Int, completion:#escaping (Bool)->Void)
{
UIView.animate(withDuration: duration, animations: {
self.contentOffset.x += xOffset
}) { (finished) in
if count <= 1
{
completion(finished)
}
else
{
self.scrollToPart(xOffset: xOffset, duration: duration, count: count-1, completion: completion)
}
}
}
I used scrollToItem with animated = true at last.

Animating cell more than once

I have a uitableview cell and it will animate when the cell is displayed. These cells are in tableviews and these tableviews are tabs of a uitabbarcontroller. But if a user presses one of the four tabs for the first time then the cells will animate, but if a user clicks a new tab and goes back to the previous tab the cells will not animate. How can I make it so that the cells animate everytime the tableview is presented. Here is my code, can someone please show me how i can make the cell animate each time the view is presented and not only when the cell is presented.
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let rotationTransform = CATransform3DScale(CATransform3DIdentity, 10, 10, 0)
cell.layer.transform = rotationTransform
UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.1, options: .curveEaseOut, animations: {
cell.layer.transform = CATransform3DIdentity
}, completion: nil)
}
Reload the data on view will appear controller method:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableview.reloadData()
}
Also, you may need to reset to the starting state before you animate again. why? because when you scroll your table view the cells that appears will not animate. they will have the final state after animation (reusable cells).
Another thing that you may scoll and the top cell will disappear before completing the animation. I am not sure what would happen. maybe it will not take its final state. and when you get back to it or that cell get reused that cell will not have the final state. but, if you reset to the first state before you start animating again that will solve all the problems.
reset animation:
cell.layer.transform = nil
// you may need to layout the cell. not sure. after this.
// then the rest of your code that animates the cell.

Cell Animation Stops when swipe left/right or pull table view out of screen

I can't figure out why animation of tableViewcell frizzes when tableView is pulled out out of screen or stoppes when you start to close menu.
To let you better understand the problem, here is a gif
I implement Tap Gesture Recognizer at custom UITableViewCell Class
let tap = UITapGestureRecognizer(target: self, action: "tapAction")
self.addGestureRecognizer(tap)
func tapAction() {
let animationWidth = leftMenuWidth * 0.27
UIView.animateWithDuration(0.75, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .AllowUserInteraction, animations: {
self.colorIndicator.frame.size.width += animationWidth
}) { (true) in
UIView.animateWithDuration(0.75, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .AllowUserInteraction, animations: {
self.colorIndicator.frame.size.width -= animationWidth
}, completion: { (true) in
print("Animation Complete")
})
}
Also i implement sliding menu by using this cocoaPods - https://github.com/jonkykong/SideMenu
Thanks.
The cells are likely being reloaded which is causing the animations to reset.
Try tracking the state of whether or not a cell has been tapped so that when it's reloaded you can show the animation as already complete. You can do this by with a simple dictionary.
At the top of your view controller, define:
private var tapped = [Int : Bool]()
Next, in cellForRowAtIndexPath: for your tableView check:
if let isSet = tapped[view.hashValue] where isSet == true {
// display animation complete. You probably don't want to re-animate
// it if it's scrolled back into view, so just get it to the completed state.
}
Finally, switch your tap action from using a gesture in the cell itself to didSelectRowAtIndexPath: inside your tableView:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// remember the cell has been tapped
tapped[view.hashValue] = true
// call your method to display the animation on the cell,
// something like cell.showAnimation(). It shouldn't animate if
// already displaying the completed animation state.
}
Also, I wouldn't recommend using += or -= operators when calculating the frame since calling it multiple times will keep growing or shrinking it. Use explicit values instead, like = animationWidth or = 0.

withRowAnimation effect disappears if user scrolls Swift

I'm facing a bugging issue concerning the insertRowsAtIndexPaths function and precisely it's withRowAnimation parameter.
With the help of the stack community i've been able to modify the effect of insertion from fast fade to a longer fade (by overriding the function), but the whole effect completely disappears if i scroll the table view upwards, hide the cells from the view and scroll back again.
Because the cells are being reused the effect is rendered totally useless.
I've tried using scrollToRowAtIndexPath function (doesn't work in this case - it initiates after the cells have already loaded, only then it scrolls down and the effect has already gone away) as well as setting the content offset to tableView.contentSize.height - the result is somewhat satisfactory, but again doesn't prevent vanishing of the insertion effect when the user scrolls.
Also willDisplayCell is not an option, because it animates the cell every single time the user scrolls and i need the animation to run just once. Urrgh)
So far i'm out of ideas of how to prevent this issue from happening.
UPDATE:
Added the suclass code
class CustomTable: UITableView{
override func insertRowsAtIndexPaths(indexPaths: [NSIndexPath], withRowAnimation animation: UITableViewRowAnimation) {
self.endUpdates()
self.beginUpdates()
for indexPath:AnyObject in indexPaths{
let cell:UITableViewCell? = (super.cellForRowAtIndexPath(indexPath as! NSIndexPath));
if cell != nil {
cell!.alpha = 0
let animationBlock = { () -> Void in
cell!.alpha = 1;
}
if UIView.respondsToSelector(Selector("animateWithDuration(duration: , delay: , usingSpringWithDamping dampingRatio: , initialSpringVelocity velocity: , options: , animations: , completion: ")){
UIView.animateWithDuration(3, delay: 3.0, usingSpringWithDamping: 1.5, initialSpringVelocity: 0.0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: animationBlock, completion: nil)
}else{
UIView.animateWithDuration(3.3, delay: 5.0, options:UIViewAnimationOptions.TransitionCrossDissolve, animations: animationBlock, completion: nil)
}
}
}
}
}

Resources