Reputation: 4667
I have a UIView
subclass where I programmatically create and add some subviews. To be exact, I have 6 subviews aligned horizontally next to each other with zero space between them. The side margins (i.e. the distance from the left border of my view to the first subview and the distance from the last subview to the right border of my view) must be 16pt and the remaining space must be distributed equally among the subviews. It should look like this:
I am trying to accomplish this using autolayout like this:
NSDictionary *viewsDict = @{@"digit1":digit1, @"digit2":digit2, @"digit3":digit3, @"digit4":digit4, @"digit5":digit5, @"digit6":digit6};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-16-[digit1][digit2(==digit1)][digit3(==digit1)][digit4(==digit1)][digit5(==digit1)][digit6(==digit1)]-16-|" options:0 metrics:nil views:viewsDict]];
When I run this on iPhone 5 with 320pt screen view I expect the following result:
Subview width: (320 - 2*16) / 6 = 48
So the frames should be:
digit1: x = 16, width = 48
digit2: x = 64, width = 48
digit3: x = 112, width = 48
digit4: x = 160, width = 48
digit5: x = 208, width = 48
digit6: x = 256, width = 48
However, at runtime a strange thing happens: my margins are wrong:
The subview frames I get are these:
digit1: x = 21, width = 48
digit2: x = 66, width = 48
digit3: x = 111, width = 48
digit4: x = 156, width = 48
digit5: x = 201, width = 48
digit6: x = 246, width = 48
So my left margin is 21pt and the right is 26pt. Also, the subviews overlap by 3pt.
I have inspected the view hierarchy with Reveal and I can confirm that there is a constraint for the margins with the correct constant of 16. But the frame is for some reason different. There also don't seem to be any other conflicting constraints, nor do I get an autolayout error when I run the app.
Can anyone see what I am missing here?
EDIT:
Testing out Aloks suggestion with using spacer views in stead of spacing constraints I tried this:
UIView *spacer1 = [[UIView alloc] init];
UIView *spacer2 = [[UIView alloc] init];
spacer1.translatesAutoresizingMaskIntoConstraints = NO;
spacer2.translatesAutoresizingMaskIntoConstraints = NO;
spacer1.backgroundColor = [UIColor redColor];
spacer2.backgroundColor = [UIColor redColor];
[self addSubview:spacer1];
[self addSubview:spacer2];
NSDictionary *viewsDict = @{@"spacer1":spacer1, @"digit1":digit1, @"digit2":digit2, @"digit3":digit3, @"digit4":digit4, @"digit5":digit5, @"digit6":digit6, @"spacer2":spacer2};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[spacer1(16)][digit1][digit2(==digit1)][digit3(==digit1)][digit4(==digit1)][digit5(==digit1)][digit6(==digit1)][spacer2(16)]|" options:0 metrics:nil views:viewsDict]];
This gives me the following result:
As you can see, the spacer views are positioned correctly, but the digit subviews remain where they were.
Upvotes: 2
Views: 534
Reputation: 4667
I've finally solved my issue, and the problem was actually elsewhere. I am not the only developer on this project and I hadn't realized (it's a big class) that someone else had been setting the position of the subviews manually in the layoutSubviews
method, like this:
- (void)layoutSubviews {
[super layoutSubviews];
// set center for each digit view
}
And of course, since the position was set after [super layoutSubviews]
it changed the positions set by my autolayout constraints.
I want to thank you all who have contributed here. Your solutions helped me confirm that my original solution was in fact correct and forced me to look for the problem elsewhere.
Upvotes: 0
Reputation: 4111
The way you have used 16 as leading
and trailing
, instead make spacer1 (UIView *spacer1) and spacer2 (UIView *spacer2)
.
write your VFL
this way:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *spacer1 = [[UIView alloc] init];
UIView *spacer2 = [[UIView alloc] init];
spacer1.translatesAutoresizingMaskIntoConstraints = NO;
spacer2.translatesAutoresizingMaskIntoConstraints = NO;
spacer1.backgroundColor = [UIColor redColor];
spacer2.backgroundColor = [UIColor redColor];
[self.view addSubview:spacer1];
[self.view addSubview:spacer2];
UIView *digit1 = [UIView new];
digit1.backgroundColor = [UIColor yellowColor];
digit1.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:digit1];
UIView *digit2 = [UIView new];
digit2.backgroundColor = [UIColor magentaColor];
digit2.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:digit2];
UIView *digit3 = [UIView new];
digit3.backgroundColor = [UIColor lightGrayColor];
digit3.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:digit3];
UIView *digit4 = [UIView new];
digit4.backgroundColor = [UIColor darkGrayColor];
digit4.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:digit4];
UIView *digit5 = [UIView new];
digit5.backgroundColor = [UIColor blueColor];
digit5.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:digit5];
UIView *digit6 = [UIView new];
digit6.backgroundColor = [UIColor brownColor];
digit6.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:digit6];
NSDictionary *viewsDict = @{@"spacer1":spacer1, @"digit1":digit1, @"digit2":digit2, @"digit3":digit3, @"digit4":digit4, @"digit5":digit5, @"digit6":digit6, @"spacer2":spacer2};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[spacer1(16)][digit1][digit2(==digit1)][digit3(==digit1)][digit4(==digit1)][digit5(==digit1)][digit6(==digit1)][spacer2(16)]|" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[spacer1(100)]" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[spacer2(100)]" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[digit1(100)]" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[digit2(100)]" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[digit3(100)]" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[digit4(100)]" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[digit5(100)]" options:0 metrics:nil views:viewsDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[digit6(100)]" options:0 metrics:nil views:viewsDict]];
}
it should work.
Upvotes: 1
Reputation: 27448
Your constraints should be like :
equal width
to every views with each other
fixed height
to every views
leading,trailing and top or bottom (which is appropriate)
to every view.
And it will work fine
See the output of this constraints
You just need to convert it in code!!!!
and if you are adding views programmatically then how you are setting view's frame is also matter. Your frame should be dynamic as per screen size.
Hope this will help :)
Upvotes: 1