I have a controller in storyboard that consist of two container views. I set the vertical distance between the two containers to zero.
What I want is to change the height constraint of one of the container at run time.
Here is the code for changing the constraint:
[UIView animateWithDuration:kAnimationDuration animations:^{
self.offeringContainerHeightConstraint.constant = [SJDataManager shared].offeringItems.count * kOfferingCellHeight + kOfferingHeaderHeight;
[self.view layoutIfNeeded];
}];
The issue is, the other container view moves before so there is blank area between the two containers until the animation completes.
I want that two container views should change their constraint value in synchronisation so that this flicker can be removed.
I have finally found the solution for this. Actually it was the difference between the expectedHeightForRowAtIndexPath and heightForRowAtIndexPath. As the height returned by these two methods are different that is why I am getting the flicker.
Related
My viewController has one view with images and labels and one textView
Im new in objective c.
My problem is to add ScrollView in my ViewController with 2 custom views(UIView and UITextView).(image in the link) I have tried many things posted here in Stack but nothing works for me.
Thank YOU!
Here is what i have :
self.scrollView.contentSize=self.scrollView.frame.size;
self.scrollView.frame=self.view.frame;
[self.view addSubview:self.scrollView];
Adjusting view's frame was the technology of 5 years ago. You should never set the frame manually, not anymore. Instead start learning Autolayout and Constraints.
These tutorials may help:
https://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2
https://www.appcoda.com/auto-layout-guide/
You are setting the content size equal to the frame size before you actually set the frame, so it's probably just 0.
You need to just switch the calls around:
self.scrollView.frame=self.view.frame;
self.scrollView.contentSize=self.scrollView.frame.size;
[self.view addSubview:self.scrollView];
The other thing to keep in mind is that because you are setting the frame of a nested view to the frame of its superview, your layout will break (or at least not do what you expect), if the origin of your superview ever changes. If the origin is 0, 0, then you are fine for the moment, but otherwise you may want to set the subview (scrollView) frame to be equal to the superview (self.view) bounds instead of the frame, like this:
self.scrollView.frame=self.view.bounds
I have a uiscrollview that has multiple subviews. They are stacked one after the next with spacing constraints. They, with auto layout, define the uiscrollview's contentsize.
Each view is my "snippet view" - a 100 px view. When a user taps a snippet view, i need to replace it with my "message view" - a much taller view. The message view has an intrinsic content size.
When I replace it, I remove all of my constraints, and then apply them again so they stack all the views on top of each other and the newly added message view gets inserted in the proper order.
This actually works fine, but its not smooth. I'd like to animate this so the snippet is removed, the views below are scooted down to make room for the taller message view, the new message view is added with the origin of the previous snippet, and I animate its frame height to fill the space.
I have code that does this without auto layout and it works well. But its a tremendous amount of layout code and I was hoping to do with auto layout.
Ive tried doing the remove / reapply constraints process, and then putting a layoutIfNeeded in an animation block. The problem is the newly added message view gets added with an origin of 0,0 and then animates down to its proper position, which is not a good effect.
You need to perform an initial layout pass to get the new view into position first.
Add it as a subview, with constraints to give it the right position (you can pin to the top of the outgoing view for this purpose). Call layoutIfNeeded, then remove and update all of your constraints and perform an animated layout as you are now.
Alternatively, before you do the animated layout, manually set the frame of the incoming view to be the same as the outgoing view. The layout pass will then animate from this instead of CGRectZero. That's probably a much neater solution.
Usually you'd add your new constraints, and then animate the application of those constraints, e.g.:
[UIView animateWithDuration:0.4 animations:^{
[self.view layoutIfNeeded];
}];
That will yield a smoother transition as the new constraints are applied.
If you want to avoid having the new subview start at 0,0 and jump down, you might create a container view (which has constraints to all the other views), and add your new view to that container. So the old "snippet view" would be in that container, you would remove it from that container, put the new one in that container, and then animate the layoutIfNeeded once all the new constraints are in place. That should avoid the effect you describe. (It should also simplify the code because you'll only be mucking about with the constraints that dictate the relationship between the container view and the subviews you add to it, and everything else should be driven from that.)
My app has a UIView called stepView that needs to grow and shrink, always expanding down and to the right, and always shrinking up and to the left. It has a subview, durationLabel, which runs along the entire width of the stepView and has a fixed height.
When the stepView is animated to grow larger, the label grows properly along with it: the text and the label's background slide to the right in sync with the growing stepView. However, when the stepView is animated to grow smaller, it immediately snaps to its new size, leaving a gap between the stepView's shrinking right edge and its right edge until the animation completes.
The label initialization code in stepView.m:
CGRect duration_frame = CGRectMake(0, 0, self.frame.size.width, HEADER_HEIGHT);
self.durationLabel = [[UILabel alloc] initWithFrame:duration_frame];
[self.durationLabel setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
[self addSubview:self.durationLabel];
And the animation code:
[UIView animateWithDuration:ANIM_DURATION
animations:^{
[stepView setFrame:newFrame];
}
];
Using bounds/center instead of frame produces the same effect. I've considered using a transform instead, but the code for calculating "newFrame" is somewhat involved, so I'd rather avoid rewriting it if possible. I've also tried manually changing the label's frame in the same animation block, but that simply makes it disappear entirely (possibly because I was trying to animate both a view's frame and its subview's frame in the same animation block?). I've also confirmed stepView and its subviews aren't undergoing any other animations at the same time, although there are animations happening to unrelated views in separate animation blocks.
I'm at a loss for why the label should animate perfectly when the stepView grows but fail to animate at all when it shrinks. I haven't seen anything in the documentation that indicates there would be a difference. Thank you for any help you can provide.
I think this is an issue with UILabel - using another UIView as subview instead of an UILabel the animation works fine also when you shrink.
If I understand correctly what you need to obtain as result, however, I would suggest you to try the following:
don't set the autoresizingMask property
// [self.durationLabel setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
set the stepView clipToBounds property to YES
self.clipToBounds = YES;
This way the UILabel frame will not change but only the part which is actually within the stepView bounds will be visible. I hope this might help.
Update: just done a quick research and found that the question is already answered here resize uiview with UILabel and animate it correctly - the answers provided seem to be aligned to what I've suggested above.
This is the setup:
A UIView created on Interface Builder, linked to an IBOutlet variable (_vAbout)
A constraint for this view that we want to animate, linked to an IBOutlet variable (_ctrBottomAboutView)
I am using this code to animate:
_ctrBottomAboutView.constant = -100;
[UIView animateWithDuration:0.5 animations:^{
[_vAbout layoutIfNeeded];
}
My problem is: whenever the view has any subviews in it, the animation doesn't work. However, if the view has no children, the animation works correctly.
Do you have any idea of a solution? I have tried everything: adding and removing constraints instead of modificating the constant value, adding constraints to the subviews on Interface Builder...
After some experiments starting from the ground with an empty project, this is what I've found:
Given A the view we want to animate and B its superview
It's very important to keep in mind that the view that receives the layoutIfNeeded message is the view that owns the constraint.
In the case of NSLayoutAttributeWidth and NSLayoutAttributeHeight the owner of the constraint is actually A, but in all the other cases, the view that owns the constraint is B
HOWEVER
If A does not have any subviews, we can call [A layoutIfNeeded] at any time on our code and it will work
If A has one or more subviews but we start the animation on viewDidLoad we can call [A layoutIfNeeded] and it will work
I have a nested view that I animate out of the frame completely on a certain action. To do this, I update the constants for the constraints in an animation block.
[UIView animateWithDuration:.5 animations:^{
self.categoriesTableViewConstraintToBottom.constant = [UIScreen mainScreen].bounds.size.height;
self.categoriesTableViewConstraintToTop.constant = [UIScreen mainScreen].bounds.size.height;
[self.view layoutSubviews];
}, completion: nil];
Basically the two constraints just tie it to the top and bottom of the parent view (which is full screen). Obviously I can't change one of these constraints without changing the other, otherwise the layout isn't satisfiable.
The weird part here is I have to change these in the order I showed above. If I update the constants in the other order, it throws an 'satisfy' error.
So:
Why does the order matter?
Are we technically supposed to remove constraints while changing them if they will conflict until we update all the constraints? (Are they evaluated immediately for some reason instead of waiting for layoutSubviews or layoutIfNeeded?