change the Vertical constraints in Iphone - ios

I have 2 views(X & Y) in my nib file. I am using Auto-Layout for this.
My second view(Y) is bottom space is 0 to view means it is at bottom of the main View.
My first view (x) is bottom space is 0 to View(Y) means X & Y conncted each other.
If I remove the view (Y) from the coding then View (X) should be place as view (y) means View (X) should be down to mani view bottom.
My try is as follow:
[vwOperation2 removeFromSuperview];
NSLog(#"%#",vwOperation2.constraints);
vwOperation2.translatesAutoresizingMaskIntoConstraints = NO;
[vwOperation2 updateConstraints];
vwOperation1.translatesAutoresizingMaskIntoConstraints = NO;
[vwOperation1 updateConstraints];
[self.view setNeedsLayout];
Help me to solve this..
Thanks in advance...

Never call updateConstraints directly.
Use rather setNeedsUpdateConstraints and needsUpdateConstraints.
Regarding the problem.
In UIView's updateConstraints method add constraints you need considering state of the view. For example:
- (void)updateConstraints {
// You can remove all the constraints here are only some of them
// You may have some IBOutlets for constraints
[self removeConstraints:self.constraints];
if (self.shouldHideX) {
// add proper constrains here
// or modify constants
} else {
// add proper constrains here
// or modify constants
}
[super updateConstraints];
}
Then in you code triggering changes you should do something like:
- (void)doSomethingAwesome {
self.shouldHideX = // determine that
[self.view setNeedsUpdateConstraints];
[self.view setNeedsLayout];
// if you want to animate that
[UIView animateWithDuration:0.2
animations:^{
[self needsUpdateConstraints];
[self layoutIfNeeded];
} completion:^(BOOL finished){
// here e.g. remove your subview from superview
}];
}

Probably my answer will be old-fashioned/not optimal, but it works.
set two IBOutlets for constraints (let's say constraintForViewY and constraintForViewX)
init them with
constraintForViewY.constant = 0
and
constraintForViewX.constant = YView.frame.size.height;
when you delete YView:
constraintForViewX.constant = 0;
(probably with animation)

Related

Reduce view and subviews animated

I am currently trying to make a UIView containing some UILabel animate to a new size. But doing so I am having some trouble understanding what is really happening with my view. I read some other post about it but I am still unclear about what is really going on.
In my button I added something that just double the size of the right constraint :
[superView layoutIfNeeded];
rightConst.constant *= 2;
[UIView animateWithDuration:3
animations:^{
[superView layoutIfNeeded];
} completion:nil];
superView Being the view I wanna animate and rightConst the constraint to the right.
But doing so, the animation starts but it is actually coming from left. I don't understand this part. My goal would be to animate just the right side of the view to show the resize and maybe the bottom part of the view but the top left should be fixed.
Thanks.
As described in this document, if you call [aView layoutSubviews], layout of the subviews of aView is forced but layout of aView itself is NOT forced.
You need to call layoutSubviews of the superview of the view you want to animate. In this case, the superview of the superview of the two labels.
Solution is here
UIView *theView;
// theView is the superview of the superview of the two labels.
theView = superView.superview;
[theView layoutIfNeeded];
rightConst.constant *= 2;
[UIView animateWithDuration:3
animations:^{
[theView layoutIfNeeded];
} completion:nil];

UIView animateWithDuration UITableView cause UITableViewCell subview animates too

I am trying to animate change UITableView height constraint using
+ transitionWithView:duration:options:animations:completion:. When the options is set, the UITableViewCell contentView's subview UIView also animates. The effect is like below. As you can see the cell underneath the window goes up and the red dot view's bounds animates to the predefined constraint. If I don't set the animation options, it won't be like this. But I need the animation when I change the UITableView height. So how to keep the table view height animation and disable the table view cell contentView's sub view animation? The code is below. The red dot view is a UIView with a red background color. Is there any way to disable UITableViewCell contentView's subview animation?
- (void)changeTableViewHeight {
self.tableViewTopVerticalSpaceConstraint.constant = 0;
[self.tableView setNeedsUpdateConstraints];
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.topViewHeightConstraint.constant = 50;
[self.topView setNeedsUpdateConstraints];
}];
}
//1. All constraints change must be done
self.hightConstraint.constant = 200;
self.anotherConstraint.constant = 34;
...
...
// 2. Indimate to iOS that this view need update constraints.
// Hey iOS I have modified few constraints. beware of it to layout that changes.
[self.view setNeedsUpdateConstraints];
// 3. I'm done, relayout the view with animation,
If I really changed any constraints to view.
[UIView animateWithDuration:1.5 animations:^{
[self.view layoutIfNeeded];
}];
Hope somebody will save their time
Had a similar problem that I solved with disabling animation for the layer of the view I didn't want animated. In your case ... implement the awakeFromNib method for your UITableViewCell subclass and disable the animations there. In your case something like ...
override func awakeFromNib() {
super.awakeFromNib()
redCircleView.layer.removeAllAnimations()
}
The thing is that UIView.animateWithDuration animates the views' layers and for new table view cells and other views created during or because of this animation, all changes will also be animated.
... EDIT ...
In the meantime I managed to find an even better solution. In the options parameter for the UIView.animateWithDuration use the UIViewAnimationOptionLayoutSubviews (or .LayoutSubviews in Swift) option. This layouts the subviews at animation commit time.
update code
- (void)changeTableViewHeight {
self.tableViewTopVerticalSpaceConstraint.constant = 0;
self.topViewHeightConstraint.constant = 50;
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
[self.topView setNeedsUpdateConstraints];
}];
}

Animate UICollectionView items on constraint change in custom layout

I'm using a custom UICollectionview Layout that displays the items in a circle. On a swipe up I reset the constraints on my collectionview and animate those constraint changes. I would like my items to animate with the constraint change so it's one smooth experience.
I get them to animate but only after the constraint change (I think), it looks messy and strange, check the screencast out here: http://cl.ly/ZmA0
This is the code in the swipe gesture method:
- (void)foo:(UISwipeGestureRecognizer *)gesture {
[self.view layoutIfNeeded];
[self.collectionView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.view.mas_width);
make.height.equalTo(self.collectionView.mas_width);
make.top.equalTo(self.view.mas_top).offset(50);
}];
[UIView animateWithDuration:1.0 animations:^{
[self.view layoutIfNeeded];
}];
[self.collectionView performBatchUpdates:^{
CircleLayout *layout = (CircleLayout *)self.collectionView.collectionViewLayout;
} completion:nil];
}
Any suggestions?
I'm not sure what MASConstraintMaker is (and the video is not working).
But within the performBatchUpdates block you are getting the collection view layout. What are you doing with it? it unused. Maybe that can point you to the right solution.

UIScrollView wrong offset with Auto Layout

I have a fairly simple view configuration:
A UIViewController, with a child UIScrollView and a UIImageView in this UIScrollView.
I set the UIImageView with a height sufficient to break out of the visible area (ie. higher to 1024pt), and set the Bottom space to superview constraint of my UIImageView to a fixed positive value (20 for example).
The whole setup works as expected, the image scrolls nicely in its parent.
Except when the view is scrolled (the effect is more visible if you scrolled to the bottom of the view), then disappear, and appear again (you switched to another view and came back) the scrolling value is restored, but the content of the scroll view is moved to the outside top part of its parent view.
This is not simple to explain, I'll try to draw it:
If you want to test/view the source (or the storyboard, I did not edit a single line of code). I put a little demo on my github: https://github.com/guillaume-algis/iOSAutoLayoutScrollView
I did read the iOS 6 changelog and the explanation on this particular topic, and think this is the correct implementation of the second option (pure auto layout), but in this case why is the UIScrollView behaving so erratically ? Am I missing something ?
EDIT: This is the exact same issue as #12580434 uiscrollview-autolayout-issue. The answers are just workarounds, as anyone found a proper way to fix this or is this a iOS bug ?
EDIT 2: I found another workaround, which keep the scroll position in the same state the user left it (this is an improvement over 12580434's accepted answer):
#interface GAViewController ()
#property CGPoint tempContentOffset;
#end
#implementation GAViewController
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.tempContentOffset = self.mainScrollView.contentOffset;
self.scrollView.contentOffset = CGPointZero;
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.scrollView.contentOffset = self.tempContentOffset;
}
This basically save the offset in viewWillAppear, reset it to the origin, and then restore the value in viewDidAppear. The problem seems to occur between these two calls, but I can't find its origin.
Yeah, something strange happened with UIScrollView in pure autolayout environment. Re-reading the iOS SDK 6.0 release notes for the twentieth time I found that:
Note that you can make a subview of the scroll view appear to float (not scroll) over the other scrolling content by creating constraints between the view and a view outside the scroll view’s subtree, such as the scroll view’s superview.
Solution
Connect your subview to the outer view. In another words, to the view in which scrollview is embedded.
As IB does not allow us set up constraints between the imageView and a view outside the scroll view’s subtree, such as the scroll view’s superview then I've done it in code.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.view removeConstraints:[self.view constraints]];
[self.scrollView removeConstraints:[self.scrollView constraints]];
[self.imageView removeConstraints:[self.imageView constraints]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[_imageView(700)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imageView(1500)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
}
And vau! It works!
The edit didn't work for me. But this worked:
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.tempContentOffset = self.scrollView.contentOffset;
self.scrollView.contentOffset = CGPointZero;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.scrollView.contentOffset = self.tempContentOffset;
}
For me I went to the IB clicked my view controller that contains the scroll view. Then I went to Attribute Inspector -> View Controller -> Extend Edges -> Uncheck "Under Top Bars" and "Under Bottom Bars".
Simple solution found, Just put
[self setAutomaticallyAdjustsScrollViewInsets:NO];
in your ViewControllers viewDidLoad method
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self setAutomaticallyAdjustsScrollViewInsets:NO];
}
I had a similar problem using a UIScrollView in a UIViewController with a top extra space that was not visible in Storyboard. The solution for me was to uncheck the "Adjust Scroll View Insets" on the ViewController storyboard properties : see answer Extra Space (inset) in Scroll View at run time
Add a global property contentOffset and save the current contentOffset in viewDidDisappear.
Once you return the method viewDidLayoutSubviews will be called and you can set your original contentOffset.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self.scrollView setContentOffset:self.contentOffset animated:FALSE];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.contentOffset = self.scrollView.contentOffset;
[self.scrollView setContentOffset:CGPointMake(0, 0) animated:FALSE];
}
Looks like the problem solved with the dispatch_async during the viewWillAppear:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CGPoint originalContentOffset = self.scrollView.contentOffset;
self.scrollView.contentOffset = CGPointZero;
dispatch_async(dispatch_get_main_queue(), ^{
self.scrollView.contentOffset = originalContentOffset;
});
}

Advice needed on moving UIViews with NSLayoutConstraints

From the images below, when the user taps on view1 I have view2 and view3 slide down to the footer, being effectively pulled by setting the view3's constraint constant with the footer to 0(ish). I have my xib set up with the constraints as shown in the first image. The 2 most important of these [for me right now] are the view1View2 constraint and the view3Footer constraint
To achieve the slide down I've ended up setting a low priority for the view1view2 constraint and a higher priority for the view3Footer constrain, then updating the view3Footer constraint constant in an animateWithDuration
My problem is getting view2 and view3 to slide back up which, if I was using the same method, I'd achieve by setting the view1view2 constraint constant to 2.
I believe that the problem with the above slide up is the greater priority of the view3Footer constraint over the view1View2 constraint, Priorities seem only to be read only so I can't change these specifically. I understand that in setting constraints I'm only requesting the view positioning.
... I believe I might be using the wrong method entirely ...
Am I going about this in the right way at all? Do I have to get the constraint objects IBOutlet and rewrite them? If so, am I rewriting the priorities? Should I just be using >= for the constants with equal priorities, which doesn't seem to work. My code for simply animating down is below, which isn't much but apart from gesture recognisers, the set up is mainly in the xib
Any help is very much appreciated.
Thanks, Steve
For the slide-down:
[UIView animateWithDuration:0.9 animations:^{
_view3FooterConstraint.constant=2;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){}];
UPDATE Also tried this setting priorities to be equal - can no longer achieve slide-down
_view3FooterConstraint.constant=2;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.9 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished){}];
For the slide-up:
[UIView animateWithDuration:0.9 animations:^{
_view1View2Constraint.constant=2;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){}];
I think I would do this by just adding and removing the 2 constraints that change (2's top constraint to 1, and 3's bottom constraint to the footer). Make sure all the views have explicit heights, and make IBOutlets to the 2 constraints I mentioned above. You really only need one of those at a time, but you need to add both in IB so you can make outlets to them. In viewDidLoad, I immediately remove the bottom one. This should work in portrait and landscape. The code I used was:
implementation ViewController {
IBOutlet NSLayoutConstraint *conBottom;
IBOutlet NSLayoutConstraint *conTop;
int pos;
}
-(void)viewDidLoad{
[super viewDidLoad];
pos = 0;
[self.view removeConstraint:conBottom];
[self.view layoutSubviews];
}
-(IBAction)togglePosition:(id)sender { //tap recognizer on view one action method
if (pos == 0) {
[self moveDown];
}else{
[self moveUp];
}
}
-(void)moveDown {
[self.view removeConstraint:conTop];
[self.view addConstraint:conBottom];
conBottom.constant=2;
[UIView animateWithDuration:0.9 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
pos = 1;
}];
}
-(void)moveUp {
[self.view removeConstraint:conBottom];
[self.view addConstraint:conTop];
conTop.constant=2;
[UIView animateWithDuration:0.9 animations:^{
[self.view layoutIfNeeded];
}completion:^(BOOL finished) {
pos = 0;
}];
}

Resources