3

When using the NSLayoutAttributeCenterX attribute, a multiplier of 1 results in a center that matches the referred view. Increasing the value moves it to the left and decreasing towards 0 moves it to the right.

I have been able to achieve roughly the results I want by trial and error (3 views evenly spaced across a super view) by using multipliers of 2.5, 1, and 0.625. While messing around I found that 200 is centered on the left edge of the referred view (also increasing more than that doesn't move the view), and 0.5 is centered on the right edge (decreasing towards 0 does move the view further left).

quick whiteboard for visualization

So here are my questions:

  1. what is this?
  2. is there a formula I can use to accurately place views this way or will it be trial and error forever?
  3. why would they do this?
cacau
  • 3,606
  • 3
  • 21
  • 42
Billy Lazzaro
  • 476
  • 2
  • 13
  • The scale is based on whatever the center of the view is. So, on an iPhone 5, the value of centerX (of the controller's self.view) would be 160 (in portrait). I wouldn't use center constraints though if I were trying to evenly space several views. – rdelmar Sep 19 '14 at 21:54

2 Answers2

10

Figuring out what the center constraint means when the multiplier is not 1.0 is tricky. I did some experimenting in Xcode 6 and here are my conclusions.

The constraint in question has the form A.center.x = B.center.x * m + c, where A and B are the views involved, m is the constraint's multiplier, and c is the constraint's constant.

Let's assume B's center.x is fixed by some other constraints. Then I believe autolayout behaves as if it used this algorithm:

  1. Find the nearest common ancestor view of A and B. Call this common ancestor G.

  2. Let bxg = B.x.center in G's coordinate system. You could compute this in code as CGFloat bxg = [G convertPoint:B.center fromView:B.superview].x.

  3. Compute axg = A's desired x-center in G's coordinate system. CGFloat axg = bxg * m + c.

  4. Convert axg to the coordinate system of A.superview, and store it as A.center.x.

OK, so given all that, how do we use center constraints to achieve your goal of “evenly spaced views”? We don't.

Here's the problem. Suppose all three of your subviews have the same width, w. (The problem is even harder if they have different widths.) And let's say the container view has width W. There are four margins (one to the left of the leftmost subview, one between the left subview and the middle subview, one between the middle subview and the right subview, and one to the right of the right subview.)

The width of the margin should thus be (W - 3 w) / 4. Then we want to somehow plug that into another constraint. You wanted to use a center constraint, but for simplicity, let's consider a left-edge constraint on the leftmost view L. we want the constraint to be L.left = (W - 3 w) / 4. This constraint is too complicated for autolayout to handle directly. Autolayout constraints can only involve two view attributes, but this involves three.

The solution is to introduce spacer views. Let's start with our three desired subviews:

starting views

I've already constrained each of these three subviews to be 80x80 and vertically centered.

Now I'll add four spacer views, shown in gray:

added spacer views

I've constrained each spacer's height and vertical center, but I have not constrained their widths. What I will do next is constrain all of the spacers to have equal widths:

equal widths

Then I'll pin each spacer's leading and trailing edges to its nearest neighbors, manually setting the constants to zero. Here's how I do the first spacer:

spacer edges

I do the same for the other three spacers. I won't show that here.

When I select the view controller and ask Xcode to update all frames, I get evenly spaced views:

update frames

Since I don't actually want the spacers to be visible at runtime, I'll select them and set them to hidden. Hidden views still participate in layout.

hide spacers

Now, what if the subviews aren't all the same width? Let's change the width of the blue subview:

change width of blue subview

Auto layout updates the frames so that the spacers continue to have equal width.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
0

I found out that if you placing view aligned to superview center X (and I believe that this would be true for Y axis too) then you can calculate multiplier coefficient from expected center of view with this:

Coeff = superview.width / view.center.X * 0.5

I.e you have superview with width = 600, you need to place a view with center on 0.25 of superview width, then center of view = 150

Coeff = 600 / 150 * 0.5 = 2

If you need symmetric view then

Coeff = 600 / 450 *0.5 = 0.666

Therefore if you know that center of view (CoV) is 0.25 then formula would be:

Coeff = 1 / CoV * 0.5
alexey.metelkin
  • 1,309
  • 1
  • 11
  • 20