Change constraints based on text length - ios

I have two labels, that are ordered like this:
H:[label1]-10-[label2]
I want to change their order, based on the first label's length. So, if its text won't fit on one line, I want to change the constraints to look like this:
V:[label1]-2.5-[label2]
Here are my constraints right now:
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_carLabel]-2.5-[_statusLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_carLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_statusLabel]|" options:0 metrics:nil views:views]];
And this is how I want them to look, when _carLabel won't fit in one line:
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_carLabel]-10-[_statusLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_carLabel]|" options:0 metrics:nil views:views]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_statusLabel]|" options:0 metrics:nil views:views]];
How would I do this?

I think your best bet would be to conditionally apply constraints based on the size of the label. I don't believe there's a way to add both constraints and have this work how you described. (Though if it is possible it will likely have to do with setting the priority of each constraint #"[label1]-10#99-[label2]" and #"V:[label1]-2.5#98-[label2] but I was unable to get that to work. The code below however seems to do what you're looking for.
UILabel *label1 = [[UILabel alloc] init];
label1.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label1];
[label1 setText:#"Small text"];
// [label1 setText:#"Some really long text to test the other scenario also works"];
UILabel *label2 = [[UILabel alloc] init];
label2.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label2];
[label2 setText:#"Small text 2"];
[label1 sizeToFit];
if (label1.frame.size.width + kPadding + label2.frame.size.width < self.view.frame.size.width) {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[label1]-padding#100-[label2]"
options:0
metrics:#{#"padding": #(kPadding)}
views:NSDictionaryOfVariableBindings(label1, label2)]];
} else {
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label1]-2.5#100-[label2]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(label1, label2)]];
}

Related

How to add two views on each other with constraints

I am trying to add 2 views with same X, Y. They have space from the edge using addConstraints:[NSLayoutConstraint constraintsWithVisualFormat...]
I tried the strings "H:|20-[A][B]-20-[C]-20-[D]-20|" and V:|20-[A][B]-20-[C]-20-[D]-20|, and centreX == CenterX and centerY==centerY, but they kept conflicting, thinking that A and B should be next to each other.
A is hidden, and on some button clicked, B is hidden and A is shown.
To add views with same centre X,Y You need to first place one of the view say bView (Bottom view), using constatins
NSArray * bVerticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)];
NSArray * bHorizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)];
This places the bView in the superview with 20 as edge offset.
Now place the tView (top View) with same centre as of bView. Here self is the superview of bView and tView
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
Then pin the edges of tView with desired offset say 40
NSArray * tVerticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|-40-[tView]-40-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)];
NSArray * tHorizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-40-[tView]-40-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)];
EDIT :
Here is how do do it.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
bView = [UIView new];
bView.backgroundColor = [UIColor redColor];
bView.translatesAutoresizingMaskIntoConstraints = NO;
tView = [UIView new];
tView.backgroundColor = [UIColor blackColor];
tView.translatesAutoresizingMaskIntoConstraints = NO;
// Firdt bView is added then tView hence tView is exaclty above the bView.
[self.view addSubview:bView];
[self.view addSubview:tView];
// Edges Constrints for bView
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[bView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(bView)]];
// Edges Constints for tView
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-20-[tView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[tView]-20-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(tView)]];
// Centring for bView and tView
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[bView]-(<=1)-[tView]" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(bView,tView)]];
}
-(IBAction)toggleViews:(id)sender {
NSArray * subViews = self.view.subviews;
[self.view exchangeSubviewAtIndex:[subViews indexOfObject:tView] withSubviewAtIndex:[subViews indexOfObject:bView]];
}

how to make horizontal spacing b/w UIfields using Autolayouts in ios

In my app I have added one text field and one button on my mainview controller using Autolayouts.
Using the below code, the horizontal spacing is not applied between UItextfild and Button. What could I have done wrong?
NSDictionary * views1 = NSDictionaryOfVariableBindings(RoundTripDateTextField,RoundTripButton);
[RoundTripBackGround addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[RoundTripDateTextField]-8-[RoundTripButton(30)]-0-|" options:0 metrics:nil views:views1]];
[RoundTripBackGround addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[RoundTripDateTextField(30)]" options:0 metrics:nil views:views1]];
[RoundTripBackGround addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[RoundTripButton(30)]" options:0 metrics:nil views:views1]];
There are some issues in your constraints. I have written a snippet using same constraints, just tweaked them a little:
UIView *RoundTripBackGround = [[UIView alloc] init];
RoundTripBackGround.translatesAutoresizingMaskIntoConstraints = NO;
RoundTripBackGround.backgroundColor = [UIColor yellowColor];
[self.view addSubview:RoundTripBackGround];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[RoundTripBackGround]|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(RoundTripBackGround)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(20)-[RoundTripBackGround]"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(RoundTripBackGround)]];
UITextField *RoundTripDateTextField = [[UITextField alloc] init];
RoundTripDateTextField.translatesAutoresizingMaskIntoConstraints = NO;
RoundTripDateTextField.backgroundColor = [UIColor blueColor];
[RoundTripBackGround addSubview:RoundTripDateTextField];
UIButton *RoundTripButton = [UIButton buttonWithType:UIButtonTypeSystem];
RoundTripButton.translatesAutoresizingMaskIntoConstraints = NO;
RoundTripButton.backgroundColor = [UIColor greenColor];
[RoundTripBackGround addSubview:RoundTripButton];
NSDictionary * views1 = NSDictionaryOfVariableBindings(RoundTripDateTextField,RoundTripButton);
[RoundTripBackGround addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[RoundTripDateTextField]-8-[RoundTripButton(30)]|"
options:0 metrics:nil
views:views1]];
[RoundTripBackGround addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[RoundTripDateTextField(30)]|"
options:0 metrics:nil
views:views1]];
[RoundTripBackGround addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[RoundTripButton(30)]|"
options:0 metrics:nil
views:views1]];
Things to notice:
Use "|[view]|" instead of "|-0-[view]-0-|".
In the last two vertical constraint you haven't set a constraint with the boundary ("V:|-0-[RoundTripDateTextField(30)]" vs "V:|[RoundTripDateTextField(30)]|").

UIScrollView does not scroll with AutoLayout

I can't make UIScrollView scroll vertically when using Autolayout, I've tried many suggestions. I've read Apple's technical note but even that doesn't seem to work with my pure auto layout approach.
Here is my simple code where I'm adding two UIView blocks1 & 2 in a container view. The container view itself is the only child of the UIScrollView as suggested by many references online. I set the heights of block 1 and 2 as 800 points each, but the scroll view won't scroll.
- (void)viewDidLoad {
[super viewDidLoad];
// Test UIScrollView with Autolayout, scrolling should word
UIView *mainView = self.view;
UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];
UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];
UIView* block1 = [[UIView alloc] init];
block1.translatesAutoresizingMaskIntoConstraints = NO;
[block1 setBackgroundColor:[UIColor blackColor]];
[contentView addSubview:block1];
UIView* block2 = [[UIView alloc] init];
block2.translatesAutoresizingMaskIntoConstraints = NO;
[block2 setBackgroundColor:[UIColor blackColor]];
[contentView addSubview:block2];
NSDictionary* viewDict = NSDictionaryOfVariableBindings(mainView,scrollView, contentView, block1, block2);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[block1(==300)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[block1(==800)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[block2(==300)]" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-810-[block2(==800)]" options:0 metrics:0 views:viewDict]];
}
Looking to your code, It seems that the size of you content view will be of same size as UIView.
e.g. : IF Device screen height is 568 then your content view size will be 568, and you are set your block2 at y position = 180.
Your Code:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(==mainView)]|" options:0 metrics:0 views:viewDict]];
change:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView(2000)]|” options:0 metrics:0 views:viewDict]];
Hope it solves your problem if any further query please let me know.
If all you want to do is make a scroll view work with auto layouts, you can try this.
http://natashatherobot.com/ios-autolayout-scrollview/
Do the following changes in your code
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-810-[block2(==800)]|" options:0 metrics:0 views:viewDict]];
Hope this helps

How do I auto layout constraints when I add a new view such as a error message to the top of a main view

This is my controller as it loaded
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.mainTitleText];
[self.view addSubview:self.subTitle];
[self.view addSubview:self.tableView];
[self.view addSubview:self.acceptBtn];
[self.view addSubview:self.notAcceptBtn];
[self addConstraints];
}
I add my constraints here
- (void)addConstraints {
id metrics = #{ #"textHMargin": #10, #"topMargin": #110, #"margin": #8, #"titleHeight": #60, #"subtitleHeight": #40, #"tableHeight": #88 };
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-textHMargin-[mainTitleText]-textHMargin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-textHMargin-[subTitle]-textHMargin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[tableView]|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-margin-[acceptBtn]-margin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-margin-[notAcceptBtn]-margin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-topMargin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
}
This is where I'm getting stuck. When I add my error message view it does not stack on top and push everything down.
- (void)acceptAction:(id)sender {
[self.activeTextfield resignFirstResponder];
if (self.airlineInfo.flightNumber && self.airlineInfo.airlineDescription) {
//TODO: success code
} else {
//show error
id metrics = #{ #"textHMargin": #10, #"topMargin": #110, #"margin": #8, #"titleHeight": #60, #"subtitleHeight": #40, #"tableHeight": #88 };
[self.view addSubview:self.infoMessage];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-textHMargin-[infoMessage]-textHMargin-|" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-topMargin-[infoMessage(20)]-margin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
}
}
view http://kgaddy.com/errorView.jpg.
What I would do is not use constraints for this extra view at all. Just stick it into the interface. But change the constant on your existing top view to a larger value, thus pushing it down and pushing down everything that is pinned to it below it.
So, right now, you are saying:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-topMargin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
Instead of just adding those constraints directly, first keep a reference to them; then add them. That way you can sneakily remove those constraints later! Then you do the same thing again but with a push-down value at the top:
[self.view removeConstraints:self.verticalContraints]; // previously saved
self.verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(40)-topMargin-[mainTitleText(titleHeight)]-margin-[subTitle(subtitleHeight)]-[tableView(tableHeight)]-margin-[acceptBtn]-margin-[notAcceptBtn]" options:0 metrics:metrics views:self.views]];
[self.view addConstraints:self.verticalConstraints];
Notice the "40" that I snuck in there. You can fix that to a better value experimentally. You see from this how to swap out a bunch of constraints and put a different set in their place.

uiscrollview doesn't scroll because of autolayout

I know that there are already many answers to my questions, but I've tested each one of these and I still have the problem.
I have a UIScrollView which contains a UIView, I want active autolayout for an animation. But because of this, my scrollview doesn't scroll.
This is my code:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_scrollView, _containerScrollView);
[_scrollView setScrollEnabled:YES];
[_scrollView setContentSize:CGSizeMake(_scrollView.frame.size.width, CGRectGetHeight(_containerScrollView.frame))];
_scrollView.userInteractionEnabled = YES;
_scrollView.delaysContentTouches = YES;
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_scrollView]|" options:0 metrics:0 views:viewsDictionary]];
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_scrollView]|" options:0 metrics:0 views:viewsDictionary]];
[_scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_containerScrollView]|" options:0 metrics:0 views:viewsDictionary]];
[_scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_containerScrollView]|" options:0 metrics:0 views:viewsDictionary]];
Somebody can explain me why doesn't it work?
Try to set your scrollView's Content size int "viewDidLayoutSubviews" method with keeping the autolayouts set.
-(void)viewDidLayoutSubviews
{
[self.itemList setContentSize:CGSizeMake(required_width, required_height)];
}

Resources