Test whether a UIView is in the middle of animation
Is there any way to tell if a UIView is in the middle of an animation? When I print out the view object while it is moving I notice that there is an "animations" entry:
search bar should end editing: <UISearchBar: 0x2e6240; frame = (0 0; 320 88); text = ''; autoresize = W+BM; animations = { position=<CABasicAnimation: 0x6a69c40>; bounds=<CABasicAnimation: 0x6a6d4d0>; }; layer = <CAL开发者_如何转开发ayer: 0x2e6e00>>
When the animation has stopped and I print the view, the "animations" entry is now gone:
search bar should end editing: <UISearchBar: 0x2e6240; frame = (0 0; 320 88); text = ''; autoresize = W+BM; layer = <CALayer: 0x2e6e00>>
A UIView
has a layer (CALayer
). You can send animationKeys
to it, which will give you an array of keys which identify the animations attached to the layer. I suppose that if there are any entries, the animation(s) are running. If you want to dig even deeper have a look at the CAMediaTiming
protocol which CALayer
adopts. It does some more information on the current animation.
Important: If you add an animation with a nil
key ([layer addAnimation:animation forKey:nil]
), animationKeys
returns nil
.
iOS 9+ method, works even when layer.animationKeys
contains no keys:
let isInTheMiddleOfAnimation = UIView.inheritedAnimationDuration > 0
From the docs:
This method only returns a non-zero value if called within a UIView animation block.
Animations are attached in fact to the underlying Core Animation CALayer
class
So I think you can just check myView.layer.animationKeys
I'm not sure of the context of the question but I had was attempting to find out if a view was animating before starting a second animation to avoid skipping. However there is a UIView animation option UIViewAnimationOptionBeginFromCurrentState
that will combine the animations if necessary to give a smooth appearance. Thereby eliminating my need to know if the view was animating.
There are a lot of out-of-date answers here. I just needed to prevent a UIView
animation being started if there was one running on the same view, so I created a category on UIView
:-
extension UIView {
var isAnimating: Bool {
return (self.layer.animationKeys()?.count ?? 0) > 0
}
}
This way I can just check any view's animation status like this:-
if !myView.isAnimating {
UIView.animate(withDuration: 0.4) {
...
}
} else {
// already animating
}
This seems less fragile than keeping track of it with a state variable.
There is a hitch with the animationKeys trick.
Sometimes there could be a few animationKeys lingering after an animation is completed.
This means that a non-animating layer could return a set of animationKeys even if it isn't actually animating.
You can make sure that animationKeys are automatically removed by setting an animation's removedOnCompletion property to YES.
e.g.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"aPath"];
animation.removedOnCompletion = YES;
If you do this for all the animations you apply to your layer, it will ensure that when the layer isn't animating there are no animationKeys present.
Some of these didn't work for me. The reason for that is that these animations are asynchronous.
What I did is defined a property
@property BOOL viewIsAnimating;
And in my animation
[UIView animateWithDuration:0.25
animations:^{
viewIsAnimating = YES;
} completion:^(BOOL finished) {
if (finished) {
viewIsAnimating = NO;
}
}];
Ref to the question: UIView center position during animation
I compare the view's frame and layer.presentation()?.frame to check it is animating. If leftView is on the way to finish, the leftView.layer.presentation()?.frame does not equal to its frame:
if self.leftView.layer.presentation()?.frame == self.leftView.frame {
// the animation finished
} else {
// the animation on the way
}
But this method may not work if the view move to the end position during the animation. More condition check may be necessary.
You could query the presentation layer as suggested here My presentation layer does not match my model layer even though I have no animations
You can use the layer property of a UIView. CALayer has a property called animation keys, you can check its count if it is greater than 0.
if (view.layer.animationKeys.count) {
// Animating
}else {
// No
}
In the Documentation:
-(nullable NSArray<NSString *> *)animationKeys;
Returns an array containing the keys of all animations currently * attached to the receiver. The order of the array matches the order * in which animations will be applied.
精彩评论