While using "addConstraint" on UIButton in loop issue is coming with button x-position,
for (int ix = 0; ix<7; ix++) {
UIButton *segmentButton = [[UIButton alloc] init];
[segmentButton setTitle:[_segmentButtonTitleArray objectAtIndex:ix] forState:UIControlStateNormal];
[_segmentView addSubview:segmentButton];
[segmentButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[_segmentView addConstraint:[NSLayoutConstraint constraintWithItem:segmentButton
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_segmentView
attribute:NSLayoutAttributeWidth
multiplier:1.0/7
constant:0]];
[_segmentView addConstraint:[NSLayoutConstraint constraintWithItem:segmentButton
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:segmentButton.frame.size.height]];
[_segmentView addConstraint:[NSLayoutConstraint constraintWithItem:segmentButton
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:_segmentView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:segmentButtonXposition]];
[_segmentView addConstraint:[NSLayoutConstraint constraintWithItem:segmentButton
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_segmentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:segmentButton.layer.frame.origin.y]];
segmentButtonXposition = segmentButtonXposition +1+_segmentView.frame.size.width/7-1;
}
An the desired result is like that, but unable to produce result.
Desired Result
It sure will take the previous width after rotation. See how you are calculating the left attribute value for constraint; from segmentVIew's frame, which you are not updating after the rotation.
So if we say that for portrait orientation your computation comes as 768/7 = ~109. However for Landscape orientation this calculation is not correct and constraint.constant value should be updated with 1024/7 = ~146. But you are not doing that, so an old value is being taken.
To resolve this, you can update each constraint every time device makes a rotation. But that is going to be tedious as you need to have a variable for each constraint and then iterate through all of them.
A better approach is to keep the equal width constraint to each UILabel with each other.
EqualWidth for each lable
label1.leading = segmentView.leading
label7.trainling = segmentView.trailing
label(n).trailing = label(n+1).leading
For reference check my answer here. Through it is applied in UIScrollView but it will help you understand this logic. And i have used VFL for that which is lot more concise.
Related
Is it possible to change autolayout constraints during runtime?
I know you can change the constant, but how would you change different attributes.
For example, NSLayoutAttributeTop to NSLayoutAttributeBottom?
Here is a simple sample of what I hope to achieve, it will set a label top left, then when you press a button it will set the label bottom right.
The initial constraints work as expected, tapping the button doesn't work as expected and throws the infamous "Unable to simultaneously satisfy constraints."
Here is the code I am using:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
[self.view addConstraint:self.constraintA];
[self.view addConstraint:self.constraintB];
}
- (IBAction)tappedChange:(id)sender
{
[self.view removeConstraints:#[ self.constraintA, self.constraintB ]];
self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[self.view addConstraint:self.constraintA];
[self.view addConstraint:self.constraintB];
[self.view setNeedsLayout];
}
Thank you for your time.
You only need to perform the Remove/Recreate/Add constraint dance on iOS 7 and below. If you are writing for a modern iOS (8 and above) you can create all your constraints at once and then just set the .active property on whatever NSLayoutConstraint instance you want at any given time.
// Move to right
self.leadingConstraint.active = false;
self.trailingConstraint.active = true;
// Move to bottom
self.topConstraint.active = false;
self.bottomConstraint.active = true;
If you are using Interface Builder you can create all the constraints that will be needed (note the grayed out constraints that aren't active by default).
Then when the button is pressed you can deactivate the old constraints and activate the new ones.
If you are ever unsure about the views being shown you can pause the app execution and use the following private API in the debugger to print out a full list of views and constraints:
po [[UIWindow keyWindow] _autolayoutTrace]
Personally I like using Masonry to manage constraints in code - it's way less writing, easier to read what you wrote six months ago, and the learning curve isn't as head-splitting either.
For example:
// Define some constraints, making the top one a #property:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
// Update the top constraint to match the bottom of the superview instead of the top
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_bottom).with.offset(padding.bottom);
}];
I'm having difficulty adding some constraints automatically. Usually when I add a constraint in Storyboard I click on the item and add a constraint to it (be that align to center x or leading edges etc)
However, when I add a constraint programmatically I'm unsure how to achieve the same results. With the following code I'm hoping to make my _spinner view to be half the width and half the height of it's parent _loadingView.
[_loadingView addConstraint:[NSLayoutConstraint constraintWithItem:_spinner
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_loadingView
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
[_loadingView addConstraint:[NSLayoutConstraint constraintWithItem:_spinner
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:_loadingView
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
However running this produces some conflicts. The _loadingView has constraints stating that it should be 80% width and height of it's parentview which is the ViewController.view So _loadingView should be 80% of ViewController.view and then _spinner should be 50% of _loadingView. But by adding the constraints on the spinnerview it is causing a conflict.
So I thought it needs an x and a y coordinate so I specified the spinner to be placed in the center of the _loadingView as follows:
[_loadingView addConstraint:[NSLayoutConstraint constraintWithItem:_spinner
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:_loadingView
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0]];
[_loadingView addConstraint:[NSLayoutConstraint constraintWithItem:_spinner
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:_loadingView
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
But this still produces conflicts and the spinner constraints are not being added.
Simply adding a subview UIView from a controller creating using instantiateViewControllerWithIdentifier to the current controllers view and adding constraints to maintain the size of the new subview to cover the entire area. The code below worked in iOS 7. iOS 8 sizes the subview to a small portion of the upper left corner. iOS 7 does what I expect, which is to size the subview across the entire size of the parent view. I'd attach an image, but don't have the rep for that. Setting translatesAutoresizingMaskIntoConstraints to YES fixes the issue, but then the view does not honor the constraints and resize when orientation changes or sizing changes.
spotCheckStoresViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"visitStoresViewController"];
spotCheckStoresViewController.mainViewController = self;
spotCheckStoresViewController.dataMode = kDataModeViews;
spotCheckStoresViewController.lastRow = [self getViewsLastRow];
spotCheckStoresViewController.view.tag = -200;
currentView = spotCheckStoresViewController.view;
[self addChildViewController:spotCheckStoresViewController];
[self.view insertSubview:currentView belowSubview:_menuViewController.view];
currentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:currentView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]];
//[self.view updateConstraints];
//[self.view layoutIfNeeded];
I've tried setting the frame of the subview as well. Any ideas what might have changed in iOS 8 to alter the behavior of constraints used in this way?
In my case, the problem related to constraints labeled UIView-Encapsulated-Layout-Width and UIView-Encapsulated-Layout-Height. When I removed them, everything behaved as though my view was of zero size, with everything centered on the upper left corner of the screen. When I left them in, new constraints worked as expected. I also retained the constraints labeled _UILayoutSupportConstraint.
In my case, it was only happening on iOS 8.3. There was a conflict with existing constraints. A colleague found that I needed to first remove any existing constraints before adding the others.
[self.view removeConstraints:self.constraints];
I'm trying to have image align with text label, like so:
To do this I use constraints. I bind the arrow to the right edge, label right side to arrow, and image right side to label left side. Here is the code:
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.disclosureIndicator attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual toItem:self.view
attribute:NSLayoutAttributeRight multiplier:1.0 constant:-15.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.purposeLabel attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual toItem:self.disclosureIndicator
attribute:NSLayoutAttributeLeft multiplier:1.0 constant:-10.0]];
NSLayoutConstraint *imgConstraint = [NSLayoutConstraint constraintWithItem:self.purposeImage attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual toItem:self.purposeLabel
attribute:NSLayoutAttributeLeft multiplier:1.0 constant:-4.0];
[self.view addConstraint:imgConstraint];
But when I try to reduce label text length and sizeToFit, it doesn't work, that can be seen from frames:
If I reduce imgConstraintpriority to less then 250 the sizeToFit actually works, label frame changes, but the image does not move at all.
What can I do to fix this?
I forgot to remove the autoresizing constraints, like so:
self.purposeImage.translatesAutoresizingMaskIntoConstraints = NO;
when I did this, everything worked.
I have a view(self.printSettingsView) created from xib. I add this view as a subview to another view(self.view). I programmatically add constraints as follows:
[self.printSettingView setTranslatesAutoresizingMaskIntoConstraints: NO];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
[self.view addConstraint:leftConstraint];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topBar
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
[self.view addConstraint:topConstraint];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
[self.view addConstraint:heightConstraint];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
[self.view addConstraint:widthConstraint];
All the other constraints take effect except for height.
What could i be doing wrong here???
Thanks
Without knowing the constraints on your xib file or what you expect to happen vs what is happening (a screenshot would be helpful) it's hard to say. However I do have one suggestion where maybe the logic isn't correct.
The second constraint is pinning printViewSettings top to the bottom of the topBar, that part makes sense. The next one however sets the height of printViewSettings to the height of its superview. This may not jive with what you want because your superview contains your topBar as well and so may be larger than you expect. What you might actually want is a constraint that pins the bottom of printViewSettings to the bottom of the superview instead.
Sorry guys about the incomplete information. The problem in a gist was that i was assigning a constraint to the subview in my main view and whatever be the constant of the constraint the size of the subview remained constant. I found that the issue was the subview in turn had components(subviews) with fixed height constraints. I made them proportional to the height of the parent view and it works now.