Reputation: 2516
I am trying to achieve a layout of buttons very similar to Apple's inbuilt Calculator app:
I am using autolayout to position the buttons, and drawing a 0.5px border on each button, aiming for a 1px gap between buttons (like the calc app, closeup showing pixels above, from a retina device).
Border applied by :
btn.layer.borderWidth=0.5f;
btn.layer.borderColor=[[UIColor blackColor] CGColor];
and the layout is set up using Interface Builder.
It almost works; but there is some variability in the gap between buttons - for example the gap between the "1,2,3,thru" row and the "4,5,6,and" row is 2 pixels, but the gap between the "7,8,9" and "cl,0,@" rows is only one pixel.
Constraints are :
In interface builder and at runtime I don't see any autolayout errors; and a debugger dump of the autolayout info gives me:
po [[UIWindow keyWindow] _autolayoutTrace]
*<UIWindow:0x10908a560> - AMBIGUOUS LAYOUT
| *<UILayoutContainerView:0x109136140>
| | *<UINavigationTransitionView:0x10908ef80>
| | | *<UIViewControllerWrapperView:0x109138870>
| | | | *<UIView:0x1090e6d10>
| | | | | *<UILabel:0x1090d5bb0>
| | | | | *<UIButton:0x1090d27e0>
| | | | | | <UIButtonLabel:0x1091c9190>
| | | | | *<UIButton:0x1090844c0>
| | | | | | <UIButtonLabel:0x1091c7990>
| | | | | *<UIButton:0x109088b60>
| | | | | | <UIButtonLabel:0x1091c6190>
| | | | | *<UIButton:0x1090e2ce0>
| | | | | | <UIButtonLabel:0x1091c4990>
| | | | | *<UIButton:0x1090e4e50>
| | | | | | <UIButtonLabel:0x1091c3190>
| | | | | *<UIButton:0x1090db730>
| | | | | | <UIButtonLabel:0x1091c1990>
| | | | | *<UIButton:0x109068f50>
| | | | | | <UIButtonLabel:0x1091c0190>
| | | | | *<UIButton:0x10906db80>
| | | | | | <UIButtonLabel:0x1091be990>
| | | | | *<UIButton:0x1090d23a0>
| | | | | | <UIButtonLabel:0x1091bd190>
| | | | | *<UIButton:0x1090c8520>
| | | | | | <UIButtonLabel:0x1091bb990>
| | | | | *<UIButton:0x1090c9090>
| | | | | | <UIButtonLabel:0x1091ba190>
| | | | | *<UIButton:0x1090c5680>
| | | | | | <UIButtonLabel:0x1091b8990>
| | | | | *<UIButton:0x1090d7fa0>
| | | | | | <UIButtonLabel:0x1091b7190>
| | | | | *<UIButton:0x1090cc820>
| | | | | | <UIButtonLabel:0x1091b5990>
| | | | | *<UIButton:0x10909dda0>
| | | | | | <UIButtonLabel:0x1091b4190>
| | | | | *<UIButton:0x1090c8090>
| | | | | | <UIButtonLabel:0x1091b2990>
| | | | | *<UIButton:0x1090c8e30>
| | | | | | <UIButtonLabel:0x1091b1190>
| | | | | *<UIButton:0x1090d5390>
| | | | | | <UIButtonLabel:0x1091af990>
| | | | | *<UIButton:0x10d30d260>
| | | | | | <UIButtonLabel:0x1091ae190>
| | | | | *<UIButton:0x10909a6a0>
| | | | | | <UIButtonLabel:0x1091ac990>
| | | | | *<UIButton:0x1090cca40>
| | | | | | <UIButtonLabel:0x109161160>
| | | | | *<UIButton:0x10d30ea90>
| | | | | | <UIButtonLabel:0x109156460>
| | | | | *<UIButton:0x1090da9e0>
| | | | | | <UIButtonLabel:0x109177ad0>
| | | | | *<_UILayoutGuide:0x1090e6dd0> - AMBIGUOUS LAYOUT
| | | | | *<_UILayoutGuide:0x1090ce080> - AMBIGUOUS LAYOUT
| | <UINavigationBar:0x10907da60>
| | | <_UINavigationBarBackground:0x109087240>
| | | | <_UIBackdropView:0x1090899f0>
| | | | | <_UIBackdropEffectView:0x10908b110>
| | | | | <UIView:0x10908bbb0>
| | | | <UIImageView:0x1090877a0>
| | | <UINavigationItemView:0x10916bc30>
| | | | <UILabel:0x10917f810>
| | | <UINavigationItemButtonView:0x1090ee060>
| | | | <UILabel:0x1090ee5d0>
| | | <_UINavigationBarBackIndicatorView:0x109098c80>
Can anyone offer any hints on how to achieve a "pixel perfect" layout like this with Autolayout? Or should I be doing this programatically instead?
Upvotes: 8
Views: 3909
Reputation: 10313
I tried to set horizontal/vertical spaces between views equal to 0.5. The color of parent view acts as border color. It looks ugly in IB, it cannot layout views properly but resulting layout on device looks perfect.
In interface builder:
On simulator:
Constraints are a bit tricky. For each row:
You can apply constraints to multiple items at once. In order to apply leading/trailing spaces between buttons, place them without intersection in desired order before applying.
Upvotes: 1
Reputation: 119242
Layer borders are drawn inside the views bounds, so you aren't going to see a "gap" between the buttons, but the effect of two borders abutting each other. This isn't going to work in the non-retina simulator or devices, since it can't draw a half-pixel, and Autolayout doesn't align views on half-pixels.
For a layout like the one above I'd be giving a fixed height to the buttons (or to one button, and making the others equal it) and letting the black view take up the remaining space. It's always best to have at least one (ideally, exactly one) variable element in a full-screen layout basically to cover rounding errors.
It sounds at the moment like you have one fixed element (the black view) and the buttons are basically "take the remaining height and divide it between yourselves", which will only work if the remaining height divides perfectly.
Upvotes: 4