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]
Related
Hello guys i have one that is.
I have two buttons in controller when i remove or hidden one button i want to other button to increase size according to width.
Here i am using the auto layout so please post your answer accordingly
See the above image when i hide the button other button it should be adjust with the size.
set up all the constraints needed to create the layout like in your first screenshot
in IB you select the "time-button" and the "pay by cash" button and add a constraint to align their trailing edges
select this new "trailing-edges-constraint" and set it's priority to high (750)
(repeat the steps 1-3 for the "pay now" button but this time align to the leading edges)
and that's it!
FDStackView
Use UIStackView as if it supports iOS 6. It will automatically replace the symbol for UIStackView into FDStackView at runtime before iOS 9.
Or you can using a array of UIView to hold the buttons you want. The first and last element of the array is a 1px width 0.01 alpha UIView. Making the first's left align to the left of screen and the last's right align to the right of screen.
The all you need to do is that put the buttons in the array and make sure every buttons are between the 1px views.
you can add this constraint manually by creating its outlet of "width constraint" like below :
And wherever you want to re-set its width then you can do it manually,
- (IBAction)buttonClicked:(id)sender
{
[UIView animateWithDuration:0.15 animations:^{
_widthConstraint.constant = 100;
}];
[self.view layoutIfNeeded];
[self.view updateConstraints];
}
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.
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.
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).
I have a view controller with 12 UITextFields.
It fits a 3.5" display very well.
I need to set it for iPhone 5 (4 inch display) such that all UITextFields cover the whole UIView by adding extra space in between.
I am trying to do this by auto layout but it is not working properly.
This is my code :
- (void) viewWillLayoutSubviews
{
int h = txt1.bounds.size.height * 12;
float unusedHorizontalSpace = self.view.bounds.size.height - h ;
NSNumber* spaceBetweenEachButton= [NSNumber numberWithFloat: unusedHorizontalSpace / 13 ] ;
NSMutableArray *constraintsForButtons = [[NSMutableArray alloc] init];
[constraintsForButtons addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat: #"V:|-50-[txt1(==30)]-(space)-[txt2(==txt1)]-(space)-[txt3(==txt1)]-(space)-[txt4(==txt1)]-(space)-[txt5(==txt1)]-(space)-[txt6(==txt1)]-(space)-[txt7(==txt1)]-(space)-[txt8(==txt1)]-(space)-[txt9(==txt1)]-(space)-[txt10(==txt1)]-(space)-[txt11(==txt1)]-(space)-[txt12]-(space)-|"
options: NSLayoutFormatAlignAllCenterX
metrics: #{#"space":spaceBetweenEachButton}
views: NSDictionaryOfVariableBindings(txt1,txt10,txt11,txt12,txt2,txt3,txt4,txt5,txt6, txt7,txt8,txt9)]];
[self.view addConstraints:constraintsForButtons];
}
If I do [txt12(==txt1)] then it displays the same as the 3.5" screen and leaves space below.
Where I am making a mistake?
To do this with auto layout, you must create extra views to fill the spaces between the text fields.
Recall that an auto layout constraint is basically the linear equation A = m * B + c. A is an attribute of one view (for example, the Y coordinate of viewA's bottom edge) and B is an attribute of another view (for example, the Y coordinate of viewB's top edge). m and c are constants. So, for example, to lay out viewA and viewB so that there are 30 points between the bottom of viewA and the top of viewB, we could create a constraint where m is 1 and c is -30.
The problem you're having is that you want to use the same value for c across 13 different constraints, and you want auto layout to compute that c value for you. Auto layout simply can't do that. Not directly. Auto layout can only compute the attributes of views; it cannot compute the m and c constants.
There is a way to make auto layout put the views where you want: reify the spaces between the text fields as additional (invisible) views. Here's an example with just 3 text fields:
We'll create a constraint to pin each spacer's top edge to the bottom edge of the text field above it. We'll also create a constraint to pin each spacer's bottom edge to the top edge of the text field below it. And finally, we'll create a constraint to force each spacer to have the same height as the topmost spacer.
We'll need a two instance variables to set things up: an array of the text fields (in order from top to bottom), and a reference to the topmost spacer view:
#implementation ViewController {
NSMutableArray *textFields;
UIView *topSpacer;
}
We'll create the text fields and spacers in code since it's hard to show a xib in a stackoverflow answer. We kick things off in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.translatesAutoresizingMaskIntoConstraints = NO;
[self addTextFields];
[self addSpacers];
}
Since we're going to use auto layout, we need to turn off translatesAutoresizingMaskIntoConstraints to prevent the system from creating extra constraints.
We create each text field, give it some dummy text, and set up constraints for its horizontal position and size:
- (void)addTextFields {
textFields = [NSMutableArray array];
for (int i = 0; i < 12; ++i) {
[self addTextField];
}
}
- (void)addTextField {
UITextField *field = [[UITextField alloc] init];
field.backgroundColor = [UIColor colorWithHue:0.8 saturation:0.1 brightness:0.9 alpha:1];
field.translatesAutoresizingMaskIntoConstraints = NO;
field.text = [field description];
[self.view addSubview:field];
[field setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[field setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-[field]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(field)]];
[textFields addObject:field];
}
We'll use a loop to create the spacers too, but we create the top and bottom spacers differently from the middle spacers, because we need to pin the top and bottom spacers to the superview:
- (void)addSpacers {
[self addTopSpacer];
for (int i = 1, count = textFields.count; i < count; ++i) {
[self addSpacerFromBottomOfView:textFields[i - 1]
toTopOfView:textFields[i]];
}
[self addBottomSpacer];
}
Here's how we create the top spacer and set up its constraints. Its top edge is pinned to the superview and its bottom edge is pinned to the first (topmost) text field. We store the top spacer in the instance variable topSpacer so we can constrain the other spacers to have the same height as the top spacer.
- (void)addTopSpacer {
UIView *spacer = [self newSpacer];
UITextField *field = textFields[0];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[spacer][field]" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer, field)]];
topSpacer = spacer;
}
Here's how we actually create a spacer view. It's just a hidden view. Since we don't care about its horizontal size or position, we just pin it to the left and right edges of the superview.
- (UIView *)newSpacer {
UIView *spacer = [[UIView alloc] init];
spacer.hidden = YES; // Views participate in layout even when hidden.
spacer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:spacer];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"|[spacer]|" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer)]];
return spacer;
}
To create a “middle” spacer between two text views, we pin it to the bottom edge of the text
field above and the top edge of the text field below. We also constrain its height to equal the height of the top spacer.
- (void)addSpacerFromBottomOfView:(UIView *)overView toTopOfView:(UIView *)underView {
UIView *spacer = [self newSpacer];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[overView][spacer(==topSpacer)][underView]" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer, overView, underView, topSpacer)]];
}
To create the bottom spacer, we pin it to the last text field and to the superview. We also constrain its height to equal the height of the top spacer.
- (void)addBottomSpacer {
UIView *spacer = [self newSpacer];
UITextField *field = textFields.lastObject;
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[field][spacer(==topSpacer)]|" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer, field, topSpacer)]];
}
If you do it right, you will get a result like this:
You can find a complete example project in this github repository.
Check out PureLayout. It's designed to be the simplest and most programmer-friendly API possible for creating Auto Layout constraints in code.
In response to your specific question, PureLayout offers a two primary APIs for distributing views, one where the spacing between each view is fixed (view size varies as needed), and the other where the size of each view is fixed (spacing between views varies as needed). The latter will accomplish what you're looking for without the use of any "spacer views".
// NSArray+PureLayout.h
// ...
/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
alignedTo:(ALAttribute)alignment
withFixedSpacing:(CGFloat)spacing;
/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
alignedTo:(ALAttribute)alignment
withFixedSize:(CGFloat)size;
// ...
see developer.apple' documentation, that is having nice description about the solution, https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html see the spacing and warping in that page, i think that is nice description, so no need to explain same thing over here
EDIT
Above link is now get disabled by apple, As from iOS 9, they have introduced Stackview, which is solution for all this.
Previously in above link the answer was same as answer provided by #rob