How to set three buttons horizontally without gap using autolayout - ios

I have a requirement where , i try to set 3 buttons at the bottom of the view horizontally placed without any gap . I have attached a screen shot of how i need to show and another showing how it displays currently.
i am using the following constraint programatically to set this
NSDictionary *views = NSDictionaryOfVariableBindings(btnCreateAccount,btnForgotuserid,btnForgotPassword);
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:btnCreateAccount attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[btnCreateAccount][btnForgotuserid(==btnCreateAccount)][btnForgotPassword(==btnCreateAccount)]|" options:NSLayoutFormatAlignAllBottom metrics:nil views:views]];
Please help me in solving this issue
EDIT : In iOS 7 , see the screen shot
Thanks,
Vinod.

I have tried your code and the constraints seem to work fine. The problem might be somewhere else.
This is the code I have tried, created all buttons programmatically:
UIButton *b1 = [[UIButton alloc] init];
UIButton *b2 = [[UIButton alloc] init];
UIButton *b3 = [[UIButton alloc] init];
for (UIButton *b in #[b1, b2, b3]) {
[b setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:b];
[b.layer setBorderWidth:1];
}
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:b1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[b1]-0-[b2(==b1)]-0-[b3(==b1)]-0-|" options:NSLayoutFormatAlignAllBottom metrics:nil views:#{ #"b1":b1, #"b2":b2, #"b3":b3 }]];
Make sure you are calling setTranslatesAutoresizingMaskIntoConstraints:NO on your buttons. If you create them in the storyboard, you need to remove the implicit constraints that are added there.
Let me know how this goes and if you need more help.

Related

How do I programatically use AutoLayout to set a UIView to the same width as its SuperView?

I have a simple UIView that I want to make the same width as the containing View's width. I want to do this programatically.
I can add a constraint in the containing View that makes the SubView's Width equal to the width of container. The C# is because i am using Xamarin iOS but this AutoLayout question is not specific to that.
View.AddConstraint(NSLayoutConstraint.Create(subView,
NSLayoutAttribute.Width,
NSLayoutRelation.Equal,
this.View,
NSLayoutAttribute.Width,
1.0f, 0.0f));
However it feels more natural to control this from within the SubView as it view will always be full width. How would I do that?
When I try and create the constraint from within the SubView I use this.SuperView as the Relation but it does not work. It throws the following Exception
NSInternalInconsistencyException Reason: Unexpected use of internal
layout attribute.
I got the same NSInternalInconsistencyException when trying to add a constraint involving the superview to which I wasn't attached yet. So maybe make sure that you attach first to the superview.
As per your question about how to set UIView size similar to superView.
You can set constraints by using two different ways.
I’ve created view and added it subview to superView.
UIView *redView;
redView = [UIView new];
[redView setBackgroundColor:[UIColor redColor]];
[redView setAlpha:0.75f];
[redView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:redView];
[self.view setBackgroundColor:[UIColor blackColor]];
1.) By using visual format.
NSDictionary *dictViews = #{#"red":redView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[red]-0-|" options:0 metrics:0 views:dictViews]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[red]-0-|" options:0 metrics:0 views:dictViews]];
2.) By using layout attributes.
Here constraintWithItem:redView - is subview to which we want to set constraints and toItem:self.view - is out superview according to which we need to set constraints.
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:1.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:1.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:1.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:1.0]];
Hope this will be helpful to you. Happy Coding.

iOS Auto Layout When Loading Another Nib

I have a detail view in a story board. I want to load another Nib in that view when a certain condition is met. But when I do, the auto layout its all screwed you.
I am trying to display the UIView above the UIWebView like an overlay view. I want the UIView to have the same ratio of the device with a maximum height of 400 between the top and bottom layout guide.
Here is the code I used to load the Nib:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController.navigationBar setTintColor:[UIColor whiteColor]];
// Do any additional setup after loading the view, typically from a nib.
UINib *s = [UINib nibWithNibName:#"Square1" bundle:nil];
NSArray *array = [s instantiateWithOwner:self options:nil];
StopView *stopView = (StopView *)[array objectAtIndex:0];
[stopView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:stopView];
id topGuide = self.topLayoutGuide;
id bottomGuide = self.bottomLayoutGuide;
UIWebView *webView = self.detailWebView;
NSDictionary *views = NSDictionaryOfVariableBindings(stopView, topGuide, bottomGuide, webView);
// this is here to stop the auto layout from reporting that the guides has
// ambiguous layout
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[topGuide]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[bottomGuide]|" options:0 metrics:nil views:views]];
// center the stop view in the super view, both lines below are needed
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-(>=12)-[stopView(<=400)]-(>=12)-|"
options: 0
metrics:nil
views:views]];
// set the height to a ratio of the width
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeWidth
relatedBy:0 toItem:stopView
attribute:NSLayoutAttributeHeight
multiplier:0.66667f constant:0];
[self.view addConstraint:con2];
// center the Stop View X,Y with the super view
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0f constant:0]];
NSLog(#"Calling configureView from viewDidLoad");
[self configureView];
}
Here are some screen shots:
As you can see in the third screen shot, my background is not showing. And you can see the T from the UILabel that placed at the top in design mode.
What am I doing incorrectly?
For a vertical constraint, you need to put a "V:" in front of the string. To get a view to be 400 max, but be as big as it can given the top and bottom spacing constraints for a smaller screen, you need to use the priority of the constraint,
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=12)-[stopView(==400#900)]-(>=12)-|"
options: 0
metrics:nil
views:views]];
The system will try to make the height of stopView as close to 400 as possible while maintaining a spacing of at least 12 to the top and bottom.

How to resize custom UITableView separators on landscape and prevent from disappearing

I've decided to programmatically create my own UITableView separator lines because I need fine control over displaying a separator above and/or below each individual UITableViewCell. My tableView has static cells, so I do not create the separators in cellForRowAtIndexPath. Instead, I have propertys for each cell and in viewDidLoad, I add a top and/or bottom separator as needed. It's working, until I rotate to landscape and then the separator line does not stretch to fill the screen - it of course remains the same width it was when created. I'm not seeing how I can automatically adjust them to fit the width of the screen.
I tried adding Auto Layout constraints (leading, trailing, top/bottom), but for some reason it's not working - the width does not change, but there are no error messages logged to indicate anything is wrong with the constraints. The separator lines also sometimes disappear upon scroll or rotate, and if I comment out the auto layout constraints then they do not disappear.
So how can I make my custom cell separators always stretch to fill the device width upon rotation, and how do I prevent them from disappearing?
If it would be easier/better to create my custom cell separators in a different way, I am willing to do that. I just don't know how this can be done aside from my approach when the cells are static. I considered creating the views in the Storyboard, and setting up the constraints visually, but would that not be the equivalent of what I'm doing programmatically? If they were dynamic cells I would do it in cellForRowAtIndexPath.
//In viewDidLoad:
[self addTopSeparatorForCell:self.myCell];
//Helper method
- (void)addTopSeparatorForCell:(UITableViewCell *)cell {
UIView *topSeparator = [[UIView alloc] initWithFrame:CGRectMake(15, 1, cell.contentView.frame.size.width, 0.5)];
//add CALayer to preserve line separator visibility when row is highlighted
CALayer *backgroundColorLayer = [CALayer layer];
backgroundColorLayer.frame = topSeparator.bounds;
backgroundColorLayer.backgroundColor = [UIColor colorWithWhite:204/255.0f alpha:1].CGColor;
[topSeparator.layer addSublayer:backgroundColorLayer];
[cell.contentView addSubview:topSeparator];
//add auto layout constraints
topSeparator.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *cn = nil;
cn = [NSLayoutConstraint constraintWithItem:topSeparator
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:15];
[cell.contentView addConstraint:cn];
cn = [NSLayoutConstraint constraintWithItem:topSeparator
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0];
[cell.contentView addConstraint:cn];
cn = [NSLayoutConstraint constraintWithItem:topSeparator
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:1];
[cell.contentView addConstraint:cn];
}
EDIT: Thanks to # user1966109, we've been able to solve the issue with the lines not extending to fill the width, and now they are preserved when highlighting a cell. But one issue still remains that I haven't been able to solve, since I'm not sure why it's occurring. The separator lines disappear after scrolling down the scrolling back up. It's related to the auto layout constraints though because a previous solution which had other issues did not exhibit this problem. Here's the current solution that causes the lines to disappear. I'd appreciate it if someone knows how to prevent this problem (and preserve the other issues already resolved).
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(15#750)-[myView]-(-47#750)-|" options:0 metrics:0 views:viewsDictionary]];
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[myView(2)]-(-2#750)-|" options:0 metrics:0 views:viewsDictionary]];
You should not mix initWithFrame and Auto Layout. You can have a good result with a few lines using Visual Format Language for Auto layout:
//#interface TableViewController ()
#property (weak, nonatomic) IBOutlet UITableViewCell *cell;
//#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *myView = [[UIView alloc] init];
myView.backgroundColor = [UIColor redColor];
[self.cell.contentView addSubview:myView];
myView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(myView);
[self.cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[myView]|" options:0 metrics:0 views:viewsDictionary]];
[self.cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[myView(2)]" options:0 metrics:0 views:viewsDictionary]];
}
This handles rotation perfectly.
Edit!
Set the following constraints if using a accessory view:
//Set a negative value to the trailing space in order to display myView under the accessory view
//Those constraints work for both self.cell and self.cell.contentView (kind of odd)
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(15#750)-[myView]-(-47#750)-|" options:0 metrics:0 views:viewsDictionary]];
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[myView(2)]-(-2#750)-|" options:0 metrics:0 views:viewsDictionary]];
With the initial help of user1966109, I have figured out constraints that address all of the problems and are working well:
[cell addConstraint:[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:cell
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:indent]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:cell
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0]];
[cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[imageView(0.5)]" options:0 metrics:0 views:viewsDictionary]];

Aligning a UIImageView with Auto Layout

What I want is to add an image as a subview, then align it centered along the X axis and 10 points from the bottom of the superview. I need to use Auto Layout only, and preferably visual formatting language.
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.imageView setImage:[UIImage imageNamed:#"06-arrow-south"]];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:self.imageView];
[self addConstraints];
self.imageView.layer.borderColor = [[UIColor redColor] CGColor];
self.imageView.layer.borderWidth = 1.0;
}
- (void)addConstraints {
NSDictionary *viewsDictionary = #{#"arrowImage":self.imageView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[arrowImage(==40)]-|"
options:0
metrics:nil
views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[arrowImage(==40)]-10-|"
options:0
metrics:nil
views:viewsDictionary]];
}
Here's what I'm getting:
V:|-[arrowImage]-10-|
This aligns the image view so that it is the standard length (20pt) from the top of its superview, and 10 from the bottom. What you want is to PIN it to the bottom only:
V:[arrowImage]-10-|
I'm not sure that centering in the superview can be done with visual format, but you can create a single constraint to center it:
[self.view addConstraint:
[NSLayoutConstraint
constraintWithItem:self.imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
There's no need to set the height or width of the image view; its size will be determined from its content.
So, here's the full code for your addConstraints method:
- (void)addConstraints {
[self.view addConstraint:
[NSLayoutConstraint
constraintWithItem:self.imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
NSDictionary *viewsDictionary = #{#"arrowImage":self.imageView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[arrowImage]-10-|"
options:0
metrics:nil
views:viewsDictionary]];
}
What you currently doing is saying arrowImage should be the full size of the view minus 20px on left, right and top but be minus 10px from bottom.
The to center on x do the following.
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:arrowImage attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
Then as #Austin points out remove the need to be minus 8 from top and be minus 10 from the bottom:
V:[arrowImage]-10-|
Btw its minus 20 as default when you connect a sibling view to a parent: (see comment below )
|-[

Autolayout stops UITapGestureRecognizer?

I setup a UIImageView to accept tap gesture, which is tapped, the button shall go hidden and shows a UIActivityIndicatorView at the same position. The logic is very simple. but I have two problems:
I need to add either [fb setTranslatesAutoresizingMaskIntoConstraints:NO]; or [loading setTranslatesAutoresizingMaskIntoConstraints:NO]; to avoid constraint conflict. in order to be safe, i added both. seems UIImageView and UIActivityIndicatorView have their own builtin constraints:
"<NSLayoutConstraint:0x8d69e80 UIImageView:0x8d68700.centerX == UIView:0x8d626f0.centerX>",
"<NSLayoutConstraint:0x8d6a0f0 UIActivityIndicatorView:0x8d629b0.centerX == UIView:0x8d626f0.centerX>",
"<NSAutoresizingMaskLayoutConstraint:0x8b48d30 h=--& v=--& UIActivityIndicatorView:0x8d629b0.midX == + 18.5>",
"<NSAutoresizingMaskLayoutConstraint:0x8b495f0 h=--& v=--& UIImageView:0x8d68700.midX == + 60>"
I have to comment off either the line [fb
setTranslatesAutoresizingMaskIntoConstraints:NO]; or [loading
setTranslatesAutoresizingMaskIntoConstraints:NO];. otherwise, the UITapGestureRecognizer doesn't work anymore, why?
code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// setup the loading activity indicator;
loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[self.view addSubview:loading];
loading.hidden = YES;
// setup the fb button
fb = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"facebook.png"]];
fb.userInteractionEnabled = YES;
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] init];
[tap addTarget:self action:#selector(login:)];
[fb addGestureRecognizer:tap];
[self.view addSubview:fb];
fb.hidden = YES;
NSLayoutConstraint *c;
c = [NSLayoutConstraint constraintWithItem:fb
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:fb
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:loading
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:loading
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self.view addConstraint:c];
[fb setTranslatesAutoresizingMaskIntoConstraints:NO];
[loading setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
}
You need to call setTranslatesAutoresizingMaskIntoConstraints:NO because the autoresizing mask gets converted to constraints which conflict with the autolayout system. You usually cannot add auto layout constraints to views with autoresizing masks constraints or you'll get into incompatible constraints.
The main issue in your case is that the width and height of those issues cannot be determined. You need to add width and height constraints as well. The layout can't be determined using the centering constraints if the auto layout system can't calculate them.

Resources