Running animation on tableViewCell blocks user interaction? - ios

I'm growing a subview of my tableViewCell in an animation.
UIView.animate(withDuration: TimeInterval(timeLeft), delay: 0, options: UIViewAnimationOptions.curveLinear, animations: {
var newFrame = self.viewToScale.frame;
newFrame = CGRect(x: 0, y: newFrame.origin.y, width: newFrame.size.width + CGFloat(widthLeftToGrow) , height: newFrame.size.height)
self.viewToScale.frame = newFrame
})
I'm getting the desired animation, but interacting with my cell has become very unreliable. When I'm trying to swipe to delete I have to spam it until I seemingly swipe at a very specific "right time".
I would assume that I need to run this on a background thread, but updating the UI on the background thread is incorrect isn't it?
How can run this animation on my tableViewCell without sacrificing the users ability to interact with the cell?

change to : options: UIViewAnimationOptions.AllowUserInteraction,
AllowUserInteraction

Related

UIView Frame View and CGRect Not Working on First Call

So I am trying to slide a tableview in over top of a lower view, which happens to also to be a uitableview. I have a sandwich icon to slide it in and a close icon to slide it out.
The button switching works great. However, when clicking the close button, the first time, the tableview will not slide out, it will basically slid out and right back to where it is.
However subsequent times, after the close icon switches back to sandwich, and then I click again to bring back close, it will slide it out just fine. Why it not work on the first try.
Below is my code. As you can see my CGRect is supposed to animate and slide the view to the displaywidth X.
#objc func showNewsList(_ sender: UIButton) -> Void {
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
newsIconClose.isHidden = true
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
newsSourceTable.frame = CGRect(x: displayWidth, y: 0, width:
displayWidth, height: displayHeight + 100)
self.addNewsIcon()
})
}
Bingo, figured it out. For whatever reason, CGRect wasn't liked, but this is.
self.newsSourceTable?.frame.origin.x = 0

UIView.animate problems with view frame

I'm struggling trying to make an animation. This is what's happening: video
The back card was supposed to do the same animation as in the beginning of the video, but it's doing a completely different thing. I'm checking the UIView.frame at the beginning of the animation and it's the same as the first time the card enters, but obviously something is wrong... Here is the code:
func cardIn() {
let xPosition = (self.darkCardView?.frame.origin.x)! - 300
let yPosition = (self.darkCardView?.frame.origin.y)!
self.initialPos = self.darkCardView.frame
let height = self.darkCardView?.frame.size.height
let width = self.darkCardView?.frame.size.width
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(Double.pi/4)))!
UIView.animate(withDuration: 1.1, animations: {
self.darkCardView?.frame = CGRect(x: xPosition, y: yPosition, width: width!, height: height!)
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
})
}
func cardOut() {
let xPosition = (self.darkCardView?.frame.origin.x)! - 600
let yPosition = (self.darkCardView?.frame.origin.y)!
let height = self.darkCardView?.frame.size.height
let width = self.darkCardView?.frame.size.width
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
UIView.animate(withDuration: 1.0, delay: 0, options: .allowAnimatedContent, animations: {
self.darkCardView?.frame = CGRect(x: xPosition, y: yPosition, width: width!, height: height!)
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(-Double.pi/4)))!
}) { (true) in
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
self.darkCardView?.frame = self.initialPos
self.cardIn()
}
}
Does somebody know how can I repeat the same animation that's in the beginning of the video after cardOut function is called?
Given that we do not know where/when you are calling the above two functions I can only take a guess at what is happening.
So you have an animate in and an animate out, but what about a reset function?
Timeline:
Animate the card in.
...
Eventually animate the card out
...
Reset the cards location to off screen to the right (location before animating in for the first time)
...
animate the card in
...
Repeat, ect....
I managed to solve the animation problem by calling viewDidLoad() instead of calling self.cardIn() in the end o cardOut completion block. But I don't want to depend on calling viewDidLoad every time a new card has to enter the screen...
Also, I didn't set any properties of the back card in viewDidLoad. I only have it in the .xib file. Any ideas?
Note that if you set a view's transform to anything other than the identity transform then the view's frame rect becomes "undefined". You can neither set it to a new value nor read it's value reliably.
You should change your code to use the view's center property if you're changing the transform.
As Jerland points out in their post, you also probably need to reset your back card to it's starting position before beginning the next animation cycle. (Set it to hidden, put it's center back to the starting position and set it's transform back to the identity transform.
EDIT:
This code:
UIView.animate(withDuration: 1.0, delay: 0, options: .allowAnimatedContent, animations: {
self.darkCardView?.frame = CGRect(x: xPosition, y: yPosition, width: width!, height: height!)
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(-Double.pi/4)))!
}) { (true) in
self.darkCardView?.transform = (self.darkCardImageView?.transform.rotated(by: CGFloat(0)))!
self.darkCardView?.frame = self.initialPos
self.cardIn()
}
Does not set the transform to identity. Try changing that line to
self.darkCardView?.transform = .identity

How to show a growing sub view over a parent view

I have a view as follows
The colored tiles are basically views. When i long press on the tiled views i am creating a copy of the tile in which it was long pressed. Also when it is long pressed there should be a view as per the following image.
The drop area should come up from bottom part or it should grow from bottom insect of the screen.
When long pressing on the tiled view i am creating a copy of the view and i am able to make the copy successfully and drag on the screen, but i am stuck at creation of this drop area and its animation.
Can someone guide me how to implement this....
Thanks in advance...
let dropView = UIView(frame: CGRect(x:0, y:0, width:300, height:100))
dropView.frame.origin = CGPoint(x: 0, y: self.view.frame.size.height)
dropView.center = CGPoint(x:self.view.frame.size.width/2, y:dropView.center.y)
dropView.backgroundColor = UIColor.blue
self.view.addSubview(dropView)
When you want to animate it to show up use this:
UIView.animate(withDuration: 0.5, delay: 0.0, options: [.repeat, .curveEaseOut, .autoreverse], animations: {
dropView.frame.origin = CGPoint(x:dropView.frame.origin.x, y:dropView.frame.origin.y - dropView.frame.size.width - 10)
}, completion: nil)

How to control the speed of UIView animation?

I am implementing a rotating digit view like this:
https://github.com/jonathantribouharet/JTNumberScrollAnimatedView
But, instead of scrolling from say 1 to 9, I want the animation to continue scrolling (1 to 9 then back to 1 to 9), until I manually stop it.
I implement this by adding UILabel subviews 1-9, vertically stacked, and add an extra label for number 1 at the bottom. When rotating to the bottom (showing the bottom label 1), in the animation completion closure, I set the transform back to origin, showing top label 1, and restart rotating animation.
This works fine, but since each rotation (1-9-1) is separate, the animation from 1-9 accelerates and then stop at 1, and pick up speed at the next iteration. This is not what I want, I want the rotation to maintain a constant speed between the iterations.
How can I do this with UIView animation?
Here is the code:
public func startAnimation(){
UIView.setAnimationCurve(UIViewAnimationCurve.easeIn)
UIView.animate(withDuration: TimeInterval(self.loopDuration), animations: { [unowned self] in
self.container!.transform = CGAffineTransform(translationX: 0, y: CGFloat(-(self.endNumber-self.startNumber+1)*(self.size)))
}) { [unowned self] (completed) in
if self.isAnimationStopped {
self.container!.transform = CGAffineTransform(translationX: 0, y: 0)
UIView.setAnimationCurve(UIViewAnimationCurve.easeOut)
UIView.animate(withDuration: TimeInterval(self.loopDuration), animations: {
self.container!.transform = CGAffineTransform(translationX: 0, y: CGFloat(-self.targetY))
})
} else {
self.container!.transform = CGAffineTransform(translationX: 0, y: 0)
self.startAnimation()
}
}
}
By default UIView animation uses ease-in, ease-out animation timing, where the animation accelerates smoothly, runs, then decelerates to a stop.
You need to use a longer form of UIView.animate, animate(withDuration:delay:options:animations:completion:) that takes an options parameter, and specify a timing curve of .curveLinear.
Just specify 0.0 for the delay and options: .curveLinear
Edit:
Note that if you need to specify multiple options, like .curveLinear and .allowUserInteraction you would use OptionSet syntax, like options: [.curveLinear, .allowUserInteraction]

Simple collectionView animation

How can i animate the loading of cells into a collectionview.
Ive been playing with the following code but it only animates cells that are off screen when i scroll.
How to animate the all the cells not the screen to start?
let finalFrame: CGRect = cell.frame
cell.frame = CGRect(x: finalFrame.origin.x - 1000, y: -500, width: 0, height: 0)
UIView.animate(withDuration: 0.5, animations: {
cell.frame = finalFrame
})
Use performBatchUpdates(_:completion:)
And inside the performBatchUpdates block:
For items:
Insert: insertItems(at:)
Delete: deleteItems(at:)
Move/Reorder: moveItem(at:to:)
For sections:
Insert: insertSections(_:)
Delete: deleteSections(_:)
Move/Reorder: moveItem(at:to:)
If you want to further customize your animations:
1: Check this thread
2: YouTube tutorial
3: Github Project
4: Alternative UIStackView for Swift 3 Youtube Tutorial

Resources