Using constraints to center an item between two elements - ios

I tried, in interface builder, to position an image between a button and the bottom of the view, and have stay centered in different screen sizes. I could not find a way to do this, so I've tried to accomplish that using the code below, but it's not working. I can get it centered using explicit points, but if use >= it hugs the bottom and all the space is added between the image and button.
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(image, button);
NSArray *constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[button]->=1-[image]->=1-|" options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary];
for (int i = 0; i<constraintsArray.count; i++) {
[self.view addConstraint:constraintsArray[i]];
}
How can I get it to center?

Unfortunately, you can't use the >= like that, but it can be done easily in IB. Just give the image view a spacing constraint to the bottom of the superview, and a vertical spacing constraint to the button -- edit one or the other of these to have the same value as the other. Give the image view a fixed height and width constraint, and make sure that the button has no other vertical constraints (delete it/them if it does).

Related

Increase Scroll view height in middle

I have a scroll view and one Plus button is there to add the extra text fields under the add button.
Please check the screenshot below for reference.
- (IBAction)Textfield:(id)sender
{
i++;//global declaration int
UITextField *textfield=[[UITextField alloc]init];
textfield.tag=i;
NSDictionary *viewsDictionary = {#"give-textfield-name here":self.textfield};
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[give-textfield-name]-10-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:viewsDictionary];
[self.textfield addConstraints:constraints];
}
Visual constraints example:
V:|-10-[give-textfield-name]-10-|
V means vertical constraints
| viewcontroller left margin &right margin
10 giving space from left margin .
And this is very basic thing of VisualConstraints ,you need to develop it more.
And one more thing you need to update the scrollviewcontent size after adding each textfield.
set the viewcontroller as freedom height and width.[update this also]
Hope it will help to start you program.
this is the link for visual constraints.
this is the link for autolayout.
Create textfields on clicking the plus button and add tags to textfields for fetching data from it.
let objForTextfield = UITextField()
objForTextfield.frame = CGRectMake(0, (scrollView.frame.origin.y + scrollView.frame.size.height ), 100, 100)
self.view.addSubview(objForTextfield)
scrollView.contentSize = CGSizeMake(scrollView.subviews.last?.frame.origin.y)!+(scrollView.subviews.last?.frame.size.height)!
firstly you need to group this view into three uiviews.
first one contain the two text field parts you want to show on buttonclick
2.Secondone contain upper part of that text fields
3.Thirdone contain lower part of that text fields
Then setheight constraintfor first view and vertical spacing bwn
First -Second and second - third.
At first time the height constraint must be zero for the first view
And priority of vertical spacing of second - third greater than priority of First -Second.
When the buttonclicks change theheight offirst view and also change the priority of that vertical spaces.
You can override viewDidLayoutSubviews and set content size of scrollview then call [self.view layoutIfNeeded]

Superview not increasing in height based on the subviews constraint

I have a scrollview and a separate UIView where I placed a series of textFields and labels with constraints which fully occupies the top and bottom. I'm trying to adjust the UIView's height based on its subview constraints but it won't. What is happening is that the view keeps its height and force other textfields to collapse or shrink thus breaking the constraints.
Details
Each subview priority values :
compression = 750
hugging = 250
UIView priority values:
compression = 249
hugging = 749 Set to be lower than the rest.
Most of the textfields has aspect ratio constraint. This causes the field to adjust.
Each subview has vertical/top/bottom spacing between each other. The top and bottom elements has top and bottom constraints to the view as well.
What's on my code:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
/* I had to adjust the UIView's width to fill the entire self.view.*/
if(![contentView isDescendantOfView:detailsScrollView]){
CGRect r = contentView.frame;
r.size.width = self.view.frame.size.width;
contentView.frame = r;
[detailsScrollView addSubview:contentView];
}
}
Screenshots
The view
This is what currently happens. In this instance it forces the email field to shrink. If I place a height value on it, it does not shrink but the layout engine finds another element to break
Edit:
Solved
Maybe I just needed some break to freshen up a bit. I did tried using constraints before but got no luck. However thanks to the suggestion I went back setting the constraints instead of setting the frame on this one and got it finally working.
Solution:
-(void)viewDidLoad{
[detailsScrollView addSubview:contentView];
[contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
[detailsScrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView,detailsScrollView);
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil
views:viewsDictionary];
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:viewsDictionary];
NSArray *widthConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView(==detailsScrollView)]-0-|" options:0 metrics:nil views:viewsDictionary];
}
When you use interface builder to deal with the UIScrollView and its child UIView. usually a top, bottom, left and equal width constraints are set between the UIScrollView and its child which is the contentView in your case.
Without those constraints the other option is to set the content size of the UIScrollView. which was the way of using the UIScrollView before introducing constraints.
So, 1. you should add those constraints programmatically.
By using the constraints, the views frame is no longer needed to resize the views.
So, 2. remove frame setting for your content view.
I am not so happy with the way you set the frame in the viewDidLayoutMethod. if I am going to do that here I would take the frame setting out of the if statement.
The code would be as follow with no if statement:
[detailsScrollView addSubview:contentView];
// then set the constraints here after adding the subview.
Put this code anywhere but not inside your viewDidLayoutSubviews method. it will be a bigger problem than setting the frame in there inside if statement.
Note: Originally, if you are going to set frame in the viewDidLayoutSubviews
method. you should do it for all cases. for example for the if case
and the else case. because, next time this method is going to be
called the views will respond to the constraint. and lose its frame.
Another observation: if you want the view to response to its subviews constraint why you need to set the frame for it? right?
After adding the constraint you may need to call the method constraintNeedsUpdate or another related method.

How to position UIbuttons in a table like way flexibly?

I need to position UIButtons flexibly like this in a vertical list:
A
B
C
D
The issue is that it need to be flexible, like when B is missing it should look like:
A
C
D
So there should be no blank space but the UIButtons should move up
Now I position the UIButtons programmatically like this, but this is only possible in iOS7 if I turn Autolayout off
But this in turn now requires to position ALL other Elements - esp on the bottom of the screen - also programmatically, which I do not want to do.
How can I position the UIButtons in such a way WITHOUT having to programmatically position all other elements on the screen programmatically.
In Android there is the tablelayout or linearLayout which handles that automatically!
EDIT:
It's all a big pain, but this is how it works:
position all your btns nicely left aligned, under each other so you can see them nicely in IB. at this stage the vertical spacing does not matter since we delete all these constranints created by IB anyway and programmatically add the vertical spacing we want later
make sure now that there are only the constraints you want, and NO constraints created by IB that you do not want.
In particular remove all vertical spacing constraints between the btns you want to programmatically reposition. Add the constraints you DO want one by one with IB by clicking on "Add constraint" in the grey popup. After that do not touch your layout anymore - you can easily mess it up!
Use this code to reposition now A in relation to C for example. don't forget to set B invisible
UIView *superview = self.view;
NSMutableArray *mutableConstraintsArray =[[NSMutableArray alloc]init];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:A
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual toItem:C
attribute:NSLayoutAttributeTop multiplier:1.0 constant:-20.0];//C is 20 points under A
[mutableConstraintsArray addObject:constraint];
for (int i = 0; i<mutableConstraintsArray.count; i++) {
[superview addConstraint:mutableConstraintsArray[i]];
}
I used mutableConstraintsArray here so I could add multuple constraints at this time!
Lot's of effort IMHO!
Please correct/improve if there is an easier way!
Thanks to matt for pointing me in the right direction.
Now I programmatically position the UIButtons programmatically like this, but this is only possible in iOS7 if I turn Autolayout off
That's not true. You can also programmatically work with autolayout constraints. Let's say the deleted button B has a constraint to the button above it, A, and a constraint to the button below it, C. When you delete B, make a constraint (programmatically) between A and C and add that constraint to their common superview. Done! C now moves into position, D moves into position, and the rest follows.
try doing programmatically:
for( i=1 ; i<[no of buttons you want to create]; i++)
{
button =[UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(5 , y + 30, 82, 82);
button.Tag= i;
[self.view addSubView: button];
}
Now it all depends on your input, that how many button you wan't there on screen, regarding frames, you can manipulate the logic in terms of x and y coordinates to adjust correct position. and also you can use each single button using their tags....
It would be nice if you put no of buttons in array then comparing i with count of that array.
I did this and was working fine.

Auto Layout with UITabBarController

I'm trying to shift to Auto Layout in my app, but I'm having some trouble with my UITabBarController. Basically, I have two buttons on my home screen, and I want them to have equal sizes, one 50 pixels from the top of the screen and one 50 pixels from the bottom.
The issue is that when I do this:
NSArray* verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-
[buttonOne(150)]" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(buttonOne)];
[self.view addConstraints:verticalConstraints];
verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:
[buttonTwo(==buttonOne)]-50-|" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(buttonOne, buttonTwo)];
[self.view addConstraints:verticalConstraints];
The bottom button is 50 pixels from the bottom of the view, which is 568 points tall, when the height of self.view should be the screen height minus the tab bar's height (the navigation bar is hidden). The majority of this padding is eaten up by the height of the tab bar.
My questions is: Why is my view controller's view not resizing so that it doesn't count the space under my tab bar as part of its height?
Suggestion would be to make sure you adding your constraints to a container and not a top level view. ie. Add a view under the top level view and add components to that. My guess is that there are some autoresizing contraints on the top level view
I think railwayparade is correct. I implemented a UITabBar/UITabBarController replacement using auto-layout, it's called GGTabBar. I also wrote a pretty comprehensive Blog Post about how I approached the problem, and how did I use Auto Layout to solve it.

Get same result with NSLayoutConstraint as with Autosizing

In my UI I have 5 buttons at the bottom. With autosizing applied to every button like on the picutre:
I get desired results:
However, when I tried to do it with Autolayout in IB or in code like this:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_button1, _button2, _button3, _button4, _button5);
NSArray *constraints = [NSLayoutConstraint
constraintsWithVisualFormat:#"|-[_button1]-[_button2]-[_button3]-[_button4]-[_button5]-|"
options:NSLayoutFormatAlignAllBaseline
metrics:nil
views:viewsDictionary];
[self.view addConstraints:constraints];
I get this:
Even when I try to set default width, I don't get behaviour I expected.
You need to set the equal width constraint to all the buttons and set the horizontal space constraint between each buttons through IB.
To set equal height
* Select all buttons
* Editor -> Pin -> Width equally
Hope this works well, as it worked for me.
#"|-[_button1]-[_button2(==_button1)]-[_button3(==_button1)]-[_button4(==_button1)]-[_button5(==_button1)]-|"
Gets you all equal width buttons, although they will stretch to fill the space in landscape...

Resources