docsteer
docsteer

Reputation: 2516

Achieving Grid Layout of Buttons with Autolayout

I am trying to achieve a layout of buttons very similar to Apple's inbuilt Calculator app: Closeup of calc application

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.

app screenshot

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

Answers (2)

vokilam
vokilam

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:

in interface builder

On simulator:

on device

Constraints are a bit tricky. For each row:

  • set height for the 1st button
  • set equal height for all buttons
  • set equal width for all buttons (a bit different for rows with orange buttons)
  • align top edges of all buttons
  • align bottom edges of all buttons
  • set leading and trailing spaces between all buttons (including spaces to superview) to 0.5
  • set top space (to previous row) of 1st button to 0.5

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

jrturton
jrturton

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

Related Questions