Auto layout: changing a view's center x alignment programatically - ios

I have an image view that is x centered to another label above it.
After tapping a button, I want that image view to be x centered to another label. I tried connecting the constraint to an outlet and replacing it with a new NSLayoutConstraint (since I cannot modify the Second Item) then calling layoutIfNeeded on the parent view, but it doesn't seem to change.
[self.tabsView layoutIfNeeded];
self.tabIndicatorCenterXAlignment = [NSLayoutConstraint
constraintWithItem:self.tabIndicatorIV
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:_tabsBtnArray[pageNumber]
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[UIView animateWithDuration:0.5 animations:
^{
[self.tabsView layoutIfNeeded];
}];

Related

UIStackView: Loading views from xib and updating height constraint of subView did not reflecting any changes?

I have the following hierarchy in my application
- UIScrollView
- UIStackView
- UIView 1 // load with xib and added in arrangedSubviews
- UIScrollView 1.1 // horizontal scrolling, fixed height constraint 38
- UIView 1.2 // called it childView. has fixed height 0 (I load the view from xib and add it here dynamically and update its height)
- UIView 1.2.1 // called it New View
- UIView 2
- UIView 3
So my problem is when I have loaded a view from xib and added it to UIView1.2 also increased height constraint 0 to a height of newly added sub-view but nothing will happen.UIView1.2height did not update expectedly .
self.constraintChildViewHeight.constant = 95;
[self layoutIfNeeded];
NewView *newView = (NewView *)[[[NSBundle mainBundle]loadNibNamed:NSStringFromClass([FieldPhotoView class]) owner:self options:nil]objectAtIndex:0];
[newView setTranslatesAutoresizingMaskIntoConstraints:false];
[self.childView addSubview:newView];
[self applyConstraintsToParent:self.childView andSubView:newView];
Method
- (void)applyConstraintsToParent:(UIView *)parentView andSubView:(UIView *)subView {
//constraints
NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0];
[parentView addConstraint:leading];
NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0];
[parentView addConstraint:trailing];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
[parentView addConstraint:top];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-8];
[parentView addConstraint:bottom];
NSLayoutConstraint *equalWidth = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:parentView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0];
[parentView addConstraint:equalWidth];
leading.active = true;
trailing.active = true;
top.active = true;
bottom.active = true;
equalWidth.active = true;
}
#Edit1 - Child view constraints
#Edit2 - For better understanding, I want to achieve this functionality programmatically using xib's(In UIStoryBoard is just working fine.)
From your question what i understood is that you are not able to scroll your content in the scrollview 1.1.
Try the following steps :
For the scrollview 1.1
give top, bottom,leading, trailing constraints w.r.t View1.
give scrollview height constraint = 38
For the childview UIView 1.2
give top, bottom,leading, trailing constraints w.r.t scrollview1.1
pin childview w.r.t View1 for leading & trailing edges
give childview height constraints
give childview vertically center constraint.
For the newView UIView 1.2.1
Load view from nib.
Add it to the childview
Set its constraints - top, bottom, leading & trailing w.r.t childview.
This will make your content scrollable.
I have shared a sample project here: https://github.com/Abhie87/StackExchangeSample
Hope this will be helpful.
Tried to do the same and it worked as expected.
What I did:
Initial views hierarchy looks like this:
Stack view constraints are on the next image:
Each view in stack view has only its' height set by constraint (the button in last view I use to add/remove the new view to the stack view at index 0)
The view to add is loaded from .xib file called SomeView.xib view hierarchy and constraints for which are on the next image (constraint called View Height Constraint is set to 1 and is changed to 95 when SomeView is added to the stack view):
The function that is called when the button is tapped looks like this:
- (IBAction)buttonTap:(UIButton *)sender {
if (self.theView) {
[self.stackView removeArrangedSubview:self.theView];
[self.theView removeFromSuperview];
self.theView = nil;
} else {
self.theView = [[[NSBundle mainBundle] loadNibNamed:#"SomeView" owner:nil options:nil] firstObject];
self.theView.translatesAutoresizingMaskIntoConstraints = NO;
[self.stackView addSubview:self.theView];
[self.stackView insertArrangedSubview:self.theView atIndex:0];
self.theView.viewHeightConstraint.constant = 95;
}
}
Hope this will give you any suggestions in how to fix your situation

Detect UIView animation and copy it

I have two views. Lets call them view1 and view2. They are at the same level in the views hierarchy. I want view2 to always follow view1 (both in position and size). I added some constraints to do that and it works fine. The problem is with the animations. Whenever I animate view1 I want view2 to do the same thing. Note that only view1 is visible to the 'outside world', and only view1 knows about view2 so I cannot just add view2 in the animations block. Can somehow view2 copy the animations of view1?
EDIT:
view1 is animated either by changing the frame or by changing constraints and view2 should work in both cases.
- (void)addView2 {
[self.superview insertSubview:self.view2 belowSubview:self];
self.view2.translatesAutoresizingMaskIntoConstraints = NO;
[self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self.view2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
[self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self.view2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
[self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self.view2 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
[self.superview addConstraint:[NSLayoutConstraint constraintWithItem:self.view2 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
}
This is the relevant code from view1.
Animations:
[UIView animateWithDuration:kAnimationDuration animations:^{
view1.frame = some other frame;
}];
or
[UIView animateWithDuration:kAnimationDuration animations:^{
[view1 layoutIfNeeded]
}];
I dont know your animation code, I guess you are using autolayout so seems your animation act to the frame and not to the constraints involved. This can cause (to one or both view during animation) an ugly size distortion. Check your code and correct all animations by using constraints.
A simple example:
UIView.animateWithDuration(0.5, delay: 1.0, options: UIViewAnimationOptions.TransitionNone, animations: { () -> Void in
self.view1LeadingConstraint.constant = 30
}, completion: { (finished: Bool) -> Void in
})
Write [self.view layoutIfNeeded]; before animating any view :
for example,
[self.view layoutIfNeeded];
[UIView animateWithDuration:kAnimationDuration animations:^{
view1.frame = some other frame;
}];
You need to call layoutIfNeeded before animation it if you use autolayout and animating it by manipulating frames.
If it is not work then write layoutIfNeeded for other both view too and then animate
Hope this will help

UISlider bar disappears using auto layout programmatically

I created a UISlider programmatically, and after adding two auto layout constraints the slider bar no longer appears. I am seeing the nub / circle for the slider, but nothing else.
// create slider programmatically
_sliderFrame = CGRectMake(10.0f, 10.0f, 250.0f, 400.0f);
_sliderCalibrate = [[UISlider alloc] initWithFrame:_sliderFrame];
_sliderCalibrate.minimumValue = 1.0f;
_sliderCalibrate.maximumValue = 100.0f;
_sliderCalibrate.value = 50.0f;
// manually specify Auto Layout constraints in code
[_sliderCalibrate setTranslatesAutoresizingMaskIntoConstraints:NO];
// add slider to view
[self.view addSubview:_sliderCalibrate];
NSLayoutConstraint *centerSliderX = [NSLayoutConstraint constraintWithItem:_sliderCalibrate
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual toItem:_sliderCalibrate.superview
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
NSLayoutConstraint *centerSliderY = [NSLayoutConstraint constraintWithItem:_sliderCalibrate
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual toItem:_sliderCalibrate.superview
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[_sliderCalibrate.superview addConstraints:#[centerSliderX, centerSliderY]];
You shouldn't set any frame when using auto layout, so just create the slider with [UISlider new]; Add either a width constraint, or delete the centerX constraint, and add constraints to both edges of the superview to give the slider a non-zero width.
By calling setFrame:​ for initializing an object, you don't need to call setTranslatesAutoresizingMaskIntoConstraints:​ with the argument NO on views that you place manually.
Just remove this line of code.

Animating Constraint Changes, position animates but height jumps without animation

I have a custom UIView. Within that UIView I have a UILabel with a yellow background containing #"8" called labelWhite. I programmatically create 4 constraints for that UILabel: top, left, width, and height.
When I tap the UIView, I change the constant value of the height and animate the layout over 5 seconds. However, the height of the view immediately jumps to the new value, but the y position also changes and jumps to a new value immediately. Then over the 5 second animation, the y position animates back to where it should have stayed all along.
You can see a video of it happening here: http://inadaydevelopment.com/stackoverflow/Constraints0.mov
What it SHOULD do is just remain at y=0 and shrink vertically. What am I doing wrong?
EDIT:
I just discovered that my animations work exactly as intended if my subviews are UIViews, but as UILabels I get the jump-size + animate-position.
What is going on? Is there a way I can get UILabels to animate their size?
This is the code I use to modify and animate the layout:
self.constraintWhiteHeight.constant = secondaryHeight;
[UIView animateWithDuration:5.0 animations:^{
[self layoutIfNeeded];
}];
This is the code I use to create the constraints:
// ------ Top -----
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.labelWhite
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[self addConstraint:constraint];
// ------ Left -----
self.constraintWhiteLeft = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.labelWhite
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0];
// ------ Width -----
NSString *constraintString = [NSString stringWithFormat:#"H:[_labelWhite(==%.0f)]", self.bounds.size.width];
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:constraintString
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(_labelWhite)];
NSLog(#"constraints: %#", constraints);
self.constraintWhiteWidth = [constraints objectAtIndex:0];
[self.labelWhite addConstraints:constraints];
// ------ Height -----
constraintString = [NSString stringWithFormat:#"V:[_labelWhite(==%.0f)]", primaryHeight];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:constraintString
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(_labelWhite)];
self.constraintWhiteHeight = [constraints objectAtIndex:0];
[self.labelWhite addConstraints:constraints];
I don't think you're doing anything wrong, this seems to be the way that labels behave when you try to animate their height. I know I've wrestled with this problem in the past, and I can't remember if I've ever found a solution that works by animating the constraints in an animation block. The way I have gotten it to work is to use a timer or CADisplayLink to adjust the height constraint.
-(void)shrinkLabel {
self.constraintWhiteHeight.constant -= 1;
if (self.constraintWhiteHeight.constant >= secondaryHeight)
[self performSelector:#selector(shrinkLabel) withObject:nil afterDelay:.05];
}
After Edit:
Although the timer method works, it doesn't always look smooth, depending on the speed and increments you use. Another way to do this is to use a large UIView (the same size as the yellow label in your movie) with a smaller UILabel inside that has centerX and centerY constraints to the larger view. Then, animate the yellow view as usual with animateWithDuration:animations:
-(void)shrinkLabel {
self.yellowViewHeightCon.constant = secondaryHeight;
[UIView animateWithDuration:5 animations:^{
[self layoutIfNeeded];
}];
}

Table View moves around after reorientation

I'm using autolayout and i've set the options leading space to superview (constant 0), trailing space to superview (constant 0) and center horizontally to supeview in a table view.
Even though, when coming back from landscape mode, the table view becomes movable you can drag it around i doesn't seem to be attached to the edges of the superview, and it seems that happens because the table view was wider in landscape mode and when it goes back to portrait mode there's some blank room left to the right of the table view to fill the space is not longer ocuppying.
I call this code in every re orientation:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[General addLeftRightConstraintsToView:_detail1TableView inRelationToSuperview:self.navigationController.view];
}
That's a class method i'm using to update layout constraints:
+ (void)addLeftRightConstraintsToView:(UIView *)view inRelationToSuperview:(UIView *)superview
{
// Left Space to Superview
NSLayoutConstraint *leftSpaceConstraint =
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
// Right Space to Superview
NSLayoutConstraint *rightSpaceConstraint =
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[superview addConstraint:leftSpaceConstraint];
[superview addConstraint:rightSpaceConstraint];
}
Any ideas?
This works:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[_details2TableView reloadData];
}

Resources