Say I do this:
[self.collectionView setCollectionViewLayout:myNewLayout animated:YES];
What is the duration of the animation that happens?
My application of this is that I'm animating layer properties inside the cells when this changes, and because layer animations don't get caught by UIView's block-based animations, I have to do it separately with my own animation block.
It you know you're inside a UIView animation, then [CATransaction animationDuration] will give you what you need.
Swift
Use this:
CATransaction.animationDuration()
- Note:
You may need to check out and apply the timing curve too. Because it affects how the animation looks
Use this if so: CATransaction.animationTimingFunction()
Related
I am currently reading Apple's Core Animation Guide, where I found the following passage regarding layer-backed views in iOS:
If you want to use Core Animation classes to initiate animations, you must issue all of your Core Animation calls from inside a view-based animation block. The UIView class disables layer animations by default but reenables them inside animation blocks. So any changes you make outside of an animation block are not animated.
Just below the quote, the documentation includes the following code listing:
[UIView animateWithDuration:1.0 animations:^{
// Change the opacity implicitly.
myView.layer.opacity = 0.0;
// Change the position explicitly.
CABasicAnimation* theAnim = [CABasicAnimation animationWithKeyPath:#"position"];
theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position];
theAnim.toValue = [NSValue valueWithCGPoint:myNewPosition];
theAnim.duration = 3.0;
[myView.layer addAnimation:theAnim forKey:#"AnimateFrame"];
}];
which implies that both implicit and explicit animations on CALayers backing UIViews must be placed within an animation block.
However, I have found this to be patently untrue. Specifically, I have successfully implemented explicit animations using Core Animation classes outside of a UIView animation block.
Have I misunderstood this passage, or is it out-of-date, or something else?
Some additional notes:
I assume that "the UIView class disables layer animations by default but reenables them inside animation blocks" refers to the +[UIView setAnimationsEnabled:] method. When I get back to a computer that can do so, I'll check to see whether +[UIView areAnimationsEnabled] returns YES or NO.
That quote refers to the layer that is backing the view. It is not true for stand-alone layers that you create and manage yourself.
Every view on iOS is backed by a layer. When you change the view's properties, it changes the underlying layer property. By default the layer would have implicit animations, but the layer "disables" that behavior except for when you are inside of an UIView animation block. This is what that part of the documentation is referring to.
There are a couple of ways you can use Core Animation to animate a layer property. The most common is to add the animation object to the layer when you want to animate the property, but you can also make customizations through the actions dictionary and the styles dictionary if you always want to animate when a property changes. The last two would also be disabled for the layer that is backing a view.
Update
The animation is working for setEnabled=NO.
The animation for setEnabled=YES is being triggered when UIScrollView is scrolling, the UIButton is inside the scrollview and the animation for setEnabled=NO is being triggered when UIScrollView is done scrolling.
So, I think the reason why animation for setEnabled=YES is not working is because the view is moving. I am not sure but this seems to be the only logical explanation from what I have found so far. I did a test with dispatch_after() and the animation worked for setEnabled too, in other words the animation is working if it is being triggered when the view is not moving.
What I need to do ?
I have two different background images for UIButton one for UIControlStateNormal and another for UIControlStateDisabled.
I want a effect where UIButton slowly transitions over from one state to another
What have I been doing ?
BOOL enableDisable = YES;
[UIView transitionWithView:((UIButton*)object)
duration:3.3
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{ [((UIButton*)object) setEnabled:enableDisable]; }
completion:nil];
The Problem
UIButton transforms to setEnabled=NO state over the duration but no matter what I put in the options setEnabled happens almost instantly.
is there something I am missing ?
Thanks in advance for your time and response.
Unfortunately, enable or disabled state for UIView aren't part of animatable properties in apple docs. The animatable properties are:
frame, center, bounds, transform, alpha, backgroundColor, contentStretch
Reference here: UIView animation
However if you want to create custom property for animation, you can have a look at this post which describes a way to achieve it. Create a custom animatable property
I can confirm that your code works as expected both for the transition
enabled: NO -> YES
and for the transition
enabled: YES -> NO
So, my guess is that something else is happening in your app that somehow interferes with the transition. Try defining a completion block like:
...
completion:^(BOOL finished) {
NSLog(#"FINISHED? %#", finished?#"YES":#"NO");
} ];
and add a trace log before the transitionWith call or inside the animation block to check how long the transition runs from start to completion.
My guess is that while the button is transitioning something else is happening that changes its state and as a secondary effect breaks the transition. I fear that without seeing more code, it will not be possible to help you further...
Is it possible to set custom begining keyframe of animation from collada, .dae file?
For example to make an animation a half-length of animation.
Or is it possible to change animation speed during that animation?
You can change the timing of an animation so that you start at an arbitrary time, it is also possible to change the speed of an animation (before it is added to the node).
Look at the timeOffset and speed properties in the CAMediaTiming protocol (which CAAnimation confirms to)
I've previously written about how use these and the other timing properties to control the timing of an animation.
See How to manage CALayer animations throughout a hierarchy for a follow-up question that is concerned just with how to synchronize parent-child layer animations.
This is a design question concerning running dependent animations at different levels of the view hierarchy in response to animations up the hierarchy.
I have a container controller that has any number of child controllers. The parent controller organizes this content on the screen and at various points needs to change the size and positions of its children views.
I'm trying to animate each of these child views to transition its size/shape/position from their original starting point to the destination. A basic first pass with some basic animations starts to get the job done.
Things get more complicated though by the fact that some of the views being resized should also be performing animations on the contents of the view. Imagine a child view with centered content. As the child is shrunk or expanded, the centered content should be animated alongside the outer animation to compensate for the bounds changes so that the content stays centered.
To further complicate matters, I also have a mask layer for each child view that needs to be animated alongside the child’s animation. I also have some gestures that require NO animations to be used for transitions - so I need a way to sometimes animate the whole view/layer tree, and sometimes not.
So all of this gives me an architecture where I have something like the following
ContainerViewController.view
-> auxiliary and decorative views
-> WrapperView (multiple)
----> mask layer
-> Child controller view
-> subviews & layers
Now my question is really one of maintainability. I can animate each of these parts using explicit or implicit animations. What I need to figure out is what’s the best way to make sure that all of the animations being done are done using the same duration and timing function. At present, I trigger a lot of these off of property changes. In some cases the property changes come from layoutSubviews (triggered from setNeedsLayout).
So, what’s the best strategy for setting up these animations, especially the explicit ones. Is the best that I can do just picking up values from CATransaction? My fear is that not every property needs to be animated in every case (like in the auxiliary views) - I already am flipping setDisableActions on/off to force/deny some property animations.
Should CATransaction be used to trigger the setup of explicit view animations? How do I bind the parameters specified for a UIView animation to the parameters that will be used for the underlying layers? The following code seems to get the job done, but seems really ugly.
-(void) animateForReason:(enum AnimationReason) animationReason
animations:(void(^)()) animationBlock completion:(void(^)(BOOL)) completionBlock {
const auto animationDuration = 3.0; // make this long to be noticeable!
[UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionLayoutSubviews
animations:^{
[CATransaction begin];
[CATransaction setAnimationDuration:animationDuration];
animationBlock();
[CATransaction commit];
}completion:completionBlock];
}
I think that UIViewControllerTransitionCoordinator is out because I need to do all of these animations in response to user actions, not just external things, like rotations or frame changes.
There are a few options to consider:
For UIView transitions, you can pass the UIViewAnimationOptionLayoutSubviews option. This will animate the changes between subviews before and after calling layoutSubviews on the view whose frame you just changed.
Instead of using layoutSubviews at all, you could override setBounds: or setFrame: on your UIView and CALayer subclasses. This way, if they're called within an animation block, the subviews will animate together with the superview. If they're not called within an animation block, they'll update instantly.
My fear is that not every property needs to be animated in every case (like in the auxiliary views) - I already am flipping setDisableActions on/off to force/deny some property animations.
Generally, if you want it animated, put it in an animation block, and if you don't, don't. Obviously, it can get more complex than this, which is why Apple sometimes has a setter with an animated argument (like setSelected:animated:).
If you have sometimes on, sometimes off properties, follow this pattern yourself. One possible implementation:
- (void) setNumberOfWidgets:(int)widgetCount animated:(BOOL)animated {
BOOL oldAnimationValue = [UIView areAnimationsEnabled];
if (!animated) {
[UIView setAnimationsEnabled:NO];
}
// a related property which may or may not cause an animation
self.someOtherProperty = someValue;
if (!animated) {
[UIView setAnimationsEnabled:oldAnimationValue];
}
}
My question is really one of maintainability.
Yes, it sounds like you're thinking about some kind of giant object that manages all the animation possibilities for you. Resist this temptation. Let each view be responsible for animating its own subviews, and tell a view (don't let it ask) whether or not these changes should be animated.
I think that UIViewControllerTransitionCoordinator is out because I need to do all of these animations in response to user actions, not just external things, like rotations or frame changes.
Yes, that's correct. Don't use UIViewControllerTransitionCoordinator for this.
I have a UIView with subviews and want to animate only specific properties of certain views. For example, I sometimes want to call [self layoutIfNeeded] and animate only the bounds but not other properties of the view or its subviews.
The problem is that +[UIView animateWithDuration:animations] tracks subviews and all animatable properties. Is there a reasonable solution to this?
Take a look at +[UIView performWithoutAnimation:]. You specify a block of changes you wish to perform without animation and they happen immediately.
This is good for iOS7 and above, and only for UIKit animation. For dealing with animations on layer objects directly, or support for older versions of iOS, you can use the following code:
[CATransaction begin];
[CATransaction setDisableActions:YES];
//Perform any changes that you do not want to be animated
[CATransaction commit];
More on performWithoutAnimation: here and on setDisabledActions: here.
If you do not wish to alter the parent's code, you can implement the setter methods of the properties you do not wish animated, and wrap the super call with performWithoutAnimation:, like so:
- (void)setFrame:(CGRect)frame
{
[UIView performWithoutAnimation: ^ {
[super setFrame:frame];
}];
}
If you don't want the subviews to resize when the parent view bounds change, you should set each of the subviews' autoresizingMask appropriately and/or set the parent's autoresizesSubviews flag.
Otherwise you have to override the parent's layoutSubviews and modify each of the subviews' frames back to their previous values.
Since you want to do something a bit more complex than just moving a view I would absolutely suggest you step down from UIKit to Core Animation. This will allow you to create animations on specific key paths for each object.
I promise it's not that scary, by creating a few CABasicAnimations and grouping them together in a CAAnimationGroup you can add them to any UIView's CALayer. This will definitely provide the control you need.