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.
Related
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]
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 view like in attached image
Now I am adding constraints such that the UITextview in the view has to be on the right hand side of the screen when orientation is changed to landscape. On UItextview, I have added below constraints,
Trailing Space to : Superview
Bottom Space to : Superview
These constraints though displayed some warnings on ambugity, did the job for me. Below is the screenshot of landscape mode.
My problem is though the UItextview is moved to right side, I want some additional width from top of superview when it is in landscape mode. In other words, I want the UITextview to be moved a little downward from where it is now in landscape mode. I am pondering how to do that using auto layout in IB and I am not able to figure that how.
Any suggestions please.
You can do this with constraints in several ways, but there's no way to do this automatically with just constraints you make in IB. By using both the multiplier and constant values in the method, constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:, you can have one constraint that evaluates to different distances in portrait and landscape. It's a pain to do the calculations to figure out what to use for those values, so I've written a category on NSLayoutConstraint to do that. An example of one of those methods, is this:
+(NSLayoutConstraint *)topConstraintForView:(UIView *)subview viewAttribute:(NSLayoutAttribute) att superview:(UIView *)superview portraitValue:(CGFloat)pValue landscapeValue:(CGFloat)lValue {
CGFloat multiplier = (pValue - lValue)/(superview.bounds.size.height - superview.bounds.size.width);
CGFloat constant = pValue - (superview.bounds.size.height * multiplier);
NSLayoutConstraint *con = [NSLayoutConstraint constraintWithItem:subview attribute:att relatedBy:0 toItem:superview attribute:NSLayoutAttributeBottom multiplier:multiplier constant:constant];
NSLog(#"top coeffs: %f %f",multiplier,constant);
return con;
}
The way you use these, is to add the starting portrait constraint in the storyboard, but check the box, "Placeholder - Remove at build time" in the attributes inspector for the constraint, and then replace it in viewDidLoad, like this:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addConstraint:[NSLayoutConstraint topConstraintForView:self.textView viewAttribute:NSLayoutAttributeTop superview:self.view portraitValue:225 landscapeValue:50]];
}
This will automatically adjust the position of the text view based on the rotation of the device without any further code. You might have to change the width of the text fields to get everything to fit properly -- I enclosed them and the "Get" labels in a view to more easily position them as a group. That view and the text view had height and width constraints, as well as top and left for the view, and top and right for the text view. The category has methods to adjust the other constraints as well, and can be found at http://jmp.sh/b/S4exMJBWftlCeO5GybNO.
The other way to do this, is to make IBOutlets to the constraints you make in IB, and adjust them (the constant value), or delete some and remake other ones, in one of the rotation callback methods.
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).
Building on a question I had earlier.
Simple button trying to transform a label. I want it to shrink by 0.5, which works but for some reason it also moves the object as it does it. The label jumps up and to the left, then transforms.
- (IBAction)btnTest:(id)sender
{
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
lblTest.transform = CGAffineTransformScale(lblTest.transform, 0.5f,0.5f);
}completion:^(BOOL finished) {
if(finished){
NSLog(#"DONE");
}
}];
}
I'm presuming from the question that you're using auto layout: In auto layout, if you have a leading and/or top constraint, after you scale with CGAffineTransformMakeScale, the leading/top constraint will be reapplied and your control will move on you in order to ensure that the constraint is still satisfied.
You can either turn off auto layout (which is the easy answer) or you can:
wait until viewDidAppear (because constraints defined in IB be applied, and the control will be placed where we want it and its center property will be reliable);
now that we have the center of the control in question, replace the leading and top constraints with NSLayoutAttributeCenterX and NSLayoutAttributeCenterY constraints, using the values for center property to set the constant for the NSLayoutConstraint as as follows.
Thus:
// don't try to do this in `viewDidLoad`; do it in `viewDidAppear`, where the constraints
// have already been set
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self replaceLeadingAndTopWithCenterConstraints:self.imageView];
}
// Because our gesture recognizer scales the UIView, it's quite important to make
// sure that we don't have the customary top and leading constraints, but rather
// have constraints to the center of the view. Thus, this looks for leading constraint
// and if found, removes it, replacing it with a centerX constraint. Likewise if it
// finds a top constraint, it replaces it with a centerY constraint.
//
// Having done that, we can now do `CGAffineTransformMakeScale`, and it will keep the
// view centered when that happens, avoiding weird UX if we don't go through this
// process.
- (void)replaceLeadingAndTopWithCenterConstraints:(UIView *)subview
{
CGPoint center = subview.center;
NSLayoutConstraint *leadingConstraint = [self findConstraintOnItem:subview
attribute:NSLayoutAttributeLeading];
if (leadingConstraint)
{
NSLog(#"Found leading constraint");
[subview.superview removeConstraint:leadingConstraint];
[subview.superview addConstraint:[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:subview.superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:center.x]];
}
NSLayoutConstraint *topConstraint = [self findConstraintOnItem:subview
attribute:NSLayoutAttributeTop];
if (topConstraint)
{
NSLog(#"Found top constraint");
[subview.superview removeConstraint:topConstraint];
[subview.superview addConstraint:[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:subview.superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:center.y]];
}
}
- (NSLayoutConstraint *)findConstraintOnItem:(UIView *)item attribute:(NSLayoutAttribute)attribute
{
// since we're looking for the item's constraints to the superview, let's
// iterate through the superview's constraints
for (NSLayoutConstraint *constraint in item.superview.constraints)
{
// I believe that the constraints to a superview generally have the
// `firstItem` equal to the subview, so we'll try that first.
if (constraint.firstItem == item && constraint.firstAttribute == attribute)
return constraint;
// While it always appears that the constraint to a superview uses the
// subview as the `firstItem`, theoretically it's possible that the two
// could be flipped around, so I'll check for that, too:
if (constraint.secondItem == item && constraint.secondAttribute == attribute)
return constraint;
}
return nil;
}
The particulars of your implementation may vary depending upon how you've defined the constraints of the control you want to scale (in my case, leading and top were based upon the superview, which made it easier), but hopefully it illustrates the solution, to remove those constraints and add new ones based upon the center.
You could, if you didn't want to iterate through looking for the constraint in question, like I do above, define an IBOutlet for the top and leading constraints instead, which greatly simplifies the process. This sample code was taken from a project where, for a variety of reasons, I couldn't use the IBOutlet for the NSLayoutConstraint references. But using the IBOutlet references for the constraints is definitely an easier way to go (if you stick with auto layout).
For example, if you go to Interface Builder, you can highlight the constraint in question and control-drag to the assistant editor to make your IBOutlet:
If you do that, rather than iterating through all of the constraints, you now can just say, for example:
if (self.imageViewVerticalConstraint)
{
[self.view removeConstraint:self.imageViewVerticalConstraint];
// create the new constraint here, like shown above
}
Frankly, I wish Interface Builder had the ability to define constraints like these right out of the box (i.e. rather than a "leading of control to left of superview" constraint, a "center of control to left of superview" constraint), but I don't think it can be done in IB, so I'm altering my constraints programmatically. But by going through this process, I can now scale the control and not have it move around on me because of constraints.
As 0x7fffffff noted, if you apply a CATransform3DMakeScale to the layer, it will not automatically apply the constraints, so you won't see it move like if you apply CGAffineTransformMakeScale to the view. But if you do anything to reapply constraints (setNeedsLayout or do any changes to any UIView objects can cause the constraints to be reapplied), the view will move on you. So you might be able to "sneak it in" if you restore the layer's transform back to identity before constraints are reapplied, but it's probably safest to turn off autolayout or just fix the constraints.