Is there a way to perform an animation in iOS where at every frame I can do some calculations, update constraints and perform a layout (layoutIfNeeded())?
The animation I'm trying to achieve seems impossible to do using the standard way of doing a constraint animation, which internally works by calculating just two frames, the first and the last one, so layoutIfNeeded() is called just twice. In the intermediate frames, view positions are determined by simply interpolating between the first and last frames. I want to recalculate the layout at every frame.
You can always try to use CALayer animations, depending on your needs.
This is not a direct answer but answer pointing to a place where you can find answer: https://www.objc.io/issues/12-animations/
Related
This may sound like a silly question, but most of my experience with iOS development has revolved around Auto Layout being prominent, and I'm curious now (for performance purposes) how I'd go about laying out a cell without Auto Layout and using only frames (and perhaps auto-resizing masks). Turns out this is very hard to Google due to the prominence of Auto Layout.
Essentially am I setting up and adding the subviews in the initWithStyle method of the UITableViewCell subclass?
Positioning wise, am I just relying on the bounds of the contentView, and then if I want one view beside another view, would I basically do newView.frame.origin.x = CGRectGetMaxX(otherView.frame) + spacing?
What happens when I rotate? I know I can watch for rotation in viewWillTransitionToSize, but how do I go about re-positioning the cells? Simply calling tableView.reloadData() would be both expensive and not do much as the cells are laid out in initWithStyle, correct?
I'm targeting iOS 8+.
Any insight would be truly appreciated.
Actually its
newView.frame.origin.x = CGRectGetMaxX(otherView.frame) + spacing + leftMargin;
// you have to include all the spacing, including margins
Tip:
Do not rely on bounds of the contentView by default it is set to maximum width of 320, i suggest you use main screen's frame for that.
AutoLayout is our friend and can save us a lot of time.
how do I go about re-positioning the cells?
If you're planning to do it programmatically, you need to setup the new height and width of the view after the rotation.
Calling tableView.reloadData() to update the views in the cell is not expensive, that's how it works. We don't have a choice but to live with it.
Then it will be better to write the frame calculation portion in viewDidLayoutSubViews.
I'm not entirely certain I understand your question. Please let me know if this makes sense:
Adding Subviews
To add subviews programmatically, it's actually best to do so in the override of layoutSubviews by checking if the contentView has subviews already added and calling a method to add them if needed. This way, you can load the table cell via either a storyboard/nib or using initWithStyle. Alternatively, you can use a shared commonInit method that you call in both initWithCoder and initWithStyle.
Layout of Subviews
To layout your subviews programmatically, you override layoutSubviews (and remember to call super) and set up the frames in that method. This method will be called whenever the view changes size (rotation, initial presentation, etc) and will always include the current bounds of the content view. To calculate your subview frames, you can do so as you've suggested, but you need to define the subview frame and then set the frame for the view:
frameB.origin.x = CGRectGetMaxX(viewA.frame) + spacing;
viewB.frame = frameB;
And keep in mind that this will not correct for the width of frameB. Therefore, you might want to consider using CGRectDivide() instead.
Scrolling performance
That said, the big performance hit when using auto layout in table view cells is not auto layout itself, but the calculations that are done many times on the same set of data to lay it out - all on the main thread. Such as calculating frames for all the text to layout a bunch of labels relative to one another or something that uses drawRect rather than drawing to an image bitmap on a background thread and then loading the image in to a UIImageView once the drawing is complete. Without knowing what data you are displaying it is hard to guess what is causing the performance issues, but you should be sure to use profiling to determine if you are actually improving performance. Additionally, you may need to consider moving some of the number crunching and/or image rendering onto a background thread.
Good luck!
I have a viewController with three subviews.
Also I use AutoLayout and size classes.
These views are animated and change location and size.
After the animations I update a label but the whole view is redrawn so each view is in their initial position and size. This shouldn't happen.
As in Apple developer reference that says:
"The default content mode of the UILabel class is
UIViewContentModeRedraw. This mode causes the view to redraw its
contents every time its bounding rectangle changes. You can change
this mode by modifying the inherited contentMode property of the
class."
It doesn't seem clear to me how to modify the -contentMode- in order to update that label and leave the view -as is-. Can anyone give me a clue?
Thanks in advance.
It sounds like you may be using autolayout to lay out your view (e.g., via constraints in IB), but then you're manipulating your views' frames directly for your animations - is this the case? If so, you should instead be animating the constant values of your constraints, or possibly the transforms of your subviews.
If you manipulate frames directly in a view which uses autolayout, your changes will be over-written the next time the system lays out your view (e.g. after a label's text changes).
You have 3 options to overcome your issue -
Stop using AutoLayout in your Storyboard/Xib.
Not a great solution
Animate changes to the transform property of your subviews. e.g. myView.transform = CGAffineTransformMakeScale(2.0,2.0);
Useful for presentation and dismissal animations, but mixing AutoLayout and transforms has some issues pre-iOS 8.0
Add IBOutlets for the constraints you need to change in your animations. Animate changes to the constant values of those constraints.
Most robust approach but can lead to a lot of properties and code for complex animations
Try contentMode = UIViewContentModeCenter. This should prevent the redraw you're seeing. Although that may not be the contentMode you want.
Option 2 is to use CATextLayer to draw the label. It will resize very nicely with animation, but it's a lot more work to set up.
I am working on a UICollectionView that reload the cells animatedly.
I have wrote an animation code in my layout class in
- (UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
And its works perfectly i just want to know is that somehow can i introduce an delay in cells animations so it will be like first cell reaches its place than second and then third cell and so far.
So is there any specific way to achieve this?
Unfortunately, the framework is not designed to work in that way. I would not animate the frames directly – that is a dark path that leads to pain. You'd basically need to add one model to the data source backing the collection view, wait, and repeat. It shouldn't be too hard, but it is a bit hacky.
If the appearance animation you'd like to achieve is a frame animation you can adjust the initial start position instead of adding a delay.
Let's say you want your cells to fly in from the right. For the first cell you can set the initial offset to 500px, for the second 600px for the third 700px and so on. Even though they all start animating at the same time, they'll reach their positions in succession.
Hope this helps.
I'm new to core animation and I'm struggling with one thing - how to combine autolayout with core animation. Actually I've found only one sentence in the documentation of Core Animation which refers to Autolayout here is it
Remember to Update View Constraints as Part of Your Animation
If you are using constraint-based layout rules to manage the position of your views, you must remove any constraints that might interfere with an animation as part of configuring that animation. Constraints affect any changes you make to the position or size of a view. They also affect the relationships between the view and its child views. If you are animating changes to any of those items, you can remove the constraints, make the change, and then apply whatever new constraints are needed.
But as I've tried all is not as strait-forward as it may seem.
Here is my scenario.
I've designed a sliding menu which uses autolayout extensively. Here is the appearance of that view.
I'm using autolayout constraints to force proportional positioning of those items in the sliding menu. Actually there are a lot of constraints there and I didn't want to post all of those in my question, and even may be they are not needed for direct answer of this question, however if you need them I can update the post with those constraints.
The animation that you see in the gif was reached by only autolayout. I just added outlet to the height constraint of the sliding menu and changed it in this way: (the code is written using Xamarin Monotouch, but I believe it should be clear what is done here for pure iOS developers)
private void AnimateSlideMenuAppearance()
{
float height;
if (isSlideMenuShown) {
height = 0;
} else {
height = slideMenuHeight;
}
UIView.Animate (0.4,
delegate {
this.slideMenuHeightConstraint.Constant = height;
this.View.LayoutIfNeeded ();
},
delegate {
isSlideMenuShown = !isSlideMenuShown;
});
}
Now I want to get more sophisticated appearance transition. CLICK HERE to see the effect that I want to reach.
Just to try out I tried to implement the disappearing part of that animation with series of CABasicAnimations, but it was unsuccessful, I get strange behaviour.
Can anybody suggest what I should do here? Is that possible to use autolayout to calculate the positions of the views, but somehow override the animation between autolayout size changes? I mean in my concrete example instead of proportionally decreasing the sizes of all buttons in menu I need to add FadeOut animation to them, animate the bounds to zero and also radically increase begin time of the animations from button to button in order to get the effect that I want. Or may be I need to completely get rid of autolayout and manually calculate the sizes and animations?
What is the best practice in these kind of scenarios - when you have complex autolayouting and you need custom Core Animation transitions between autolayout changes? I hope that I described the question well.
Thank you for your answers.
This is completely feasible, while it may be complex solely because it looks like your desired cases will have multiple animations.
However, I noticed one thing in your code that's odd: you change the constant on the constraint (this.slideMenuHeightConstraint.Constant = height) in the animation block, instead of before it. For nearly all cases I can imagine, you should change the constraint before the animation block. Constraints are not visually rendered until either the next UI run loop (or by setNeedsUpdateConstraints to force it for the next run loop), or immediately by layoutIfNeeded. And since [UIView animate:...] is doing this for you, layoutIfNeeded should (generally) be the only thing in your animate block, when animating autolayout.
In your case, you will have to make the animation somewhat reactive, however - for e.g., if you want to add those buttons in like in the example and have them pop in, animate out, and grow. After calling layoutIfNeeded, you can safely check the frame size. If it's beyond your threshold (or some other metric), you can trigger then animations of the buttons. (So yes, this may be a case where I'd add more code inside the animate block -- check the threshold, begin other animation, etc).
I have a UIImageView whose instance has a repeating pulse-like animation.
I want this UIImageView to move with the force of the accelerometer. To do this I have added another animation, this time a translation.
However, once this translation happens, it stops the pulse, even when its not animating.
As these two animations are separate, I cannot use the CGAffineTransformConcat method to combine both animations.
I have tried adding one to the UIView and one to the layer property of the same UIView but it does not work.
I have also tried adding the movement directly to the frame of the view, however, this just causes my view to disobey the constraint constraining it to the center x of the superview.
Does anyone use/know of a work around to this?
Thanks as always!