I am trying clear the data in a UITableView with some quick animation, I am using the below code, which works fine in iOS8.
However when I run it on iOS7 it only runs the block after completion , but not the first animation code (fade out), so the animation looks very bad, the tableview disappears suddenly and returns back with animation.
any idea what is wrong here? what is the problem with it in iOS7?
UIView.animateWithDuration (0.5, animations: {
self.tableView.transform = CGAffineTransformMakeScale(0.2, 0.2)
self.tableView.alpha = 0
}, completion: { (value: Bool) in
UIView.animateWithDuration (0.5, animations: {
self.tableView.reloadData()
self.tableView.transform = CGAffineTransformMakeScale(1.0, 1.0)
self.tableView.alpha = 1
println ("animation done")
})
})
Try removing UIView.animateWithDuration (0.5, animations: { block inside completion block and check
Related
I have UITableView that update rapidly via WebSocket and RXSwift. Every update will play flash animation. Everything works well in iOS11 - iOS14 but after the iOS15 update, the animation has weird behavior. It isn't play properly. It skip most of animation updates. Sometime it play animation in all rows at the same time.
Edited: I've got another issue; when I press on the button in the cell, the action didn't fire. It take a lot of click on it to make it fired, looks like it can't touch the button while updating. Sometime I press on button in cell 1 but the action fired as cell 2 context.
(Cell information was hidden for secret)
From the video, on the left was run on iOS11-iOS14. The animation works smoothly while on the right the animation was skipped.
The code to update the animation is:
func flash()->Observable<Void>{
return Observable.create { (observer) -> Disposable in
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: []){
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
self.background.alpha = 0
}
UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.5) {
self.background.alpha = 0.2
}
UIView.addKeyframe(withRelativeStartTime: 1, relativeDuration: 0.5) {
self.background.alpha = 0
}
}
return Disposables.create()
}
}
Call it like this. I didn't dispose someBehaviorRelay because it removes the animation
someBehaviorRelay.subscribe { [unowned self] value in
flash().subscribe().disposed(by: disposeBag)
}
And I reassign disposeBage when reuse
override func prepareForReuse() {
disposeBag = DisposeBag()
}
Is there any suggestion for me to solve this problem? Thank you.
UPDATE
I found the solution is use reconfigure instead of reloadData for UITableView
// if iOS15
let indexPaths = tableView.indexPathsForVisibleRows!
if !indexPaths.isEmpty {
tableView.reconfigureRows(at: indexPaths)
} else {
tableView.reloadData()
}
I found the solution already, updated in the question.
I want to give an alpha effect when closing the button's visibility with the code below. However, in the code below, the alpha effect works correctly, but instantly becomes , without waiting for 0.5 seconds of visibility.
Do you have alternative suggestions to solve this? Especially if you have a solution with RxSwift, RxCoca, it would be nice. Thanks.
self.button.alpha = 1.0
UIView.animate(withDuration: 0.5) {
self.button.alpha = 0
self.button.isHidden = true
}
Use it like this
UIView.animate(withDuration: 0.5, animations: {
self.button.alpha = 0
}) { (_) in
self.button.isHidden = true
}
Hide the button after your view's alpha has changed to 0.
The issue in your code is that the button gets hidden in the animation block so the animation happens when the view is already hidden.
self.button.isHidden = true within the animation block is causing the immediate disappearance as this property is not animatable.
Animating the alpha is what you should keep.
Solution:
self.button.alpha = 1.0
UIView.animate(withDuration: 0.5) {
self.button.alpha = 0
}
RxSwift solution:
https://medium.com/flawless-app-stories/rxanimated-animated-bindings-c5daa7f7d591
You need to use it like this .. You need to hide button on completion of Animation instead of in Animation block because isHidden property is not animate-able so it hide button immediately
self.button.alpha = 1.0
UIView.animateKeyframes(withDuration: 0.5, delay: 0 ,animations: {
self.button.alpha = 0
}) { _ in
self.button.isHidden = true
}
There are so many posts similar to this that I have seen and none of them works.
Here is my code so far.
func startRotating(view : UIImageView, rotating : Bool) {
//Rotating the rotatingClockBarImage
UIView.animate(withDuration: 1.0, delay: 0.0, options: [.curveLinear], animations: {
view.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
}, completion: {finished in
UIView.animate(withDuration: 1.0, delay: 0.0, options: [.curveLinear], animations: {
view.transform = CGAffineTransform(rotationAngle: 0)
}, completion: nil)
})//End of rotation
continueRotating(view: view)
}
The original problem would be that I couldn't rotate a full 360 degrees. I figured that out by rotating half way and the other half in the completion.
The problem now is once this animation finishes, that's it. I have tried putting it in a while loop, for loop, calling two similar functions back and forth. Nothing works it just keeps freezing my app.
In a for loop, for example, that would run 3 times, I put a print(). The print writes to the console three times but the animation only happens once. Because of this I am thinking the animation is just cutting itself off before it even starts, and the final rotation is the only one that plays through. So I need to find a way to allow it to play each rotation through.
This shouldn't be that hard seeing that Apple had their planes rotate so easily in a former version of Xcode in a game app. I'm trying to avoid deleting and reinstalling the old version just so I can look at that code.
Actually it would be more easy:
extension UIView {
func rotate360Degrees(duration: CFTimeInterval = 1.0) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat.pi * 2
rotateAnimation.duration = duration
rotateAnimation.repeatCount = Float.infinity
self.layer.add(rotateAnimation, forKey: nil)
}
func stopRotating(){
self.layer.sublayers?.removeAll()
//or
self.layer.removeAllAnimations()
}
}
Then for rotating:
yourView.rotate360Degrees()
for stopping:
yourView. stopRotating()
Did you try calling startRotating again in the completion block of your second half turn ?
Note that you should do that conditionally with a "stop" flag of your own if you ver want it to stop.
I have a animation that I want to be infinite
var rotateRock: Bool = true
func rotateRock() {
UIView.animate(withDuration: 2.7, delay: 2.7, options: [UIViewAnimationOptions.repeat, UIViewAnimationOptions.curveLinear], animations:
{
self.rockImg.transform.ty += 155
if self.rotateRock {
self.rotateRock = false
self.rockImg.transform = CGAffineTransform(rotationAngle: (180.0 * CGFloat(M_PI)) / 180.0)
} else {
self.rotateRock = true
self.rockImg.transform = CGAffineTransform(rotationAngle: 0)
}
},completion: nil)
}
Then I call rotateRock() inside viewDidLoad() this works, the animation never stops which is what I want.
If I now enter another VC that is presented modally then exit it so I end up in my first VC the animation is still running which I want.
But if I change VC by clicking on another tab in my tab bar and then go back the animation has stopped. Is there any way to check if the animation has stopped so that I can restart it?
You could override viewDidAppear() and call rotateRock there. That should work.
Edit:
What Matt says below is true, viewDidAppear would work but maybe it would be better viewWillAppear to make sure you don't see it before starting.
I'm expecting the completion closure on this UIView animation to be called after the specified duration, however it appears to be firing immediately...
UIView.animateWithDuration(
Double(0.2),
animations: {
self.frame = CGRectMake(0, -self.bounds.height, self.bounds.width, self.bounds.height)
},
completion: { finished in
if(finished) {
self.removeFromSuperview()
}
}
)
Has anyone else experienced this? I've read that others had more success using the center rather than the frame to move the view, however I had the same problems with this method too.
For anyone else that is having a problem with this, if anything is interrupting the animation, the completion closure is immediately called. In my case, this was due to a slight overlap with a modal transition of the view controller that the custom segue was unwinding from. Using the delay portion of UIView.animateWithDuration(0.3, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations:{} had no effect for me. I ended up using GCD to delay animation a fraction of a second.
// To avoid overlapping with the modal transiton
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.2 * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
// Animate the transition
UIView.animateWithDuration(0.3, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
// Animations
}, completion: { finished in
// remove the views
if finished {
blurView.removeFromSuperview()
snapshot.removeFromSuperview()
}
})
})
I resolved this in the end by moving the animation from hitTest() and into touchesBegan() in the UIView