The last few days I’ve been poring over the Core Animation Programming Guide. In particular, I’ve been trying to figure out how to create a grid of equally-sized cells. My initial attempt just saw me calculating the frames for each cell in the grid manually, and explicitly setting the frame for each cell. This worked, after a fashion, but was pretty ugly. And, if I resized the grid the cells didn’t follow.
Enter CAConstraintLayoutManager. I initially tried to constrain each cell to neighbouring cells, and to the super layer for the “outer” cells. Not only was this messy; I couldn’t make it work at all—not even with a 1 by 5 “grid”. I suspect that the layout manager is getting a bit confused because I don’t specify the size of any dimension of any cell.
Then I discovered the scale-argument form of the NSConstraint. With this I can specify the size of a cell and its position in the grid only in terms of the super layer. This is much much simpler. Have a look at the example code below. Paste it into a custom view, and it should display a gray grid on a pleasant blue background:
- (void)awakeFromNib {
self.wantsLayer = YES;
CALayer *grid = self.layer;
grid.backgroundColor = CGColorCreateGenericRGB(0.1, 0.1, 0.4, .8);
grid.layoutManager = [CAConstraintLayoutManager layoutManager];
int rows = 8;
int columns = 8;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < columns; c++) {
CALayer *cell = [CALayer layer];
cell.borderColor = CGColorCreateGenericGray(0.8, 0.8);
cell.borderWidth = 1;
cell.cornerRadius = 4;
cell.name = [NSString stringWithFormat:@"%u@%u", c, r];
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintWidth
relativeTo: @"superlayer"
attribute: kCAConstraintWidth
scale: 1.0 / columns
offset: 0]];
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintHeight
relativeTo: @"superlayer"
attribute: kCAConstraintHeight
scale: 1.0 / rows
offset: 0]];
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintMinX
relativeTo: @"superlayer"
attribute: kCAConstraintMaxX
scale: c / (float)columns
offset: 0]];
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintMinY
relativeTo: @"superlayer"
attribute: kCAConstraintMaxY
scale: r / (float)rows
offset: 0]];
[grid addSublayer:cell];
}
}
}
Things aren’t so simple if you want to have some spacing between the cells. I suspect you have to pack a cell within an intermediate CALayer, or add some custom drawing to each cell to draw the border a bit inset from the edge of the cell.
Update: It turns out that using this method to create a grid with spacing between the cells is not much harder after all. The below replacements for the constraints above give you a 2-pixel spacing between the tiles. I’ve highlighted the differences in bold text.
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintWidth
relativeTo: @"superlayer"
attribute: kCAConstraintWidth
scale: 1.0 / columns
offset: -2]];
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintHeight
relativeTo: @"superlayer"
attribute: kCAConstraintHeight
scale: 1.0 / rows
offset: -2]];
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintMidX
relativeTo: @"superlayer"
attribute: kCAConstraintMaxX
scale: (c + 0.5) / (float)columns
offset: 0]];
[cell addConstraint:
[CAConstraint constraintWithAttribute: kCAConstraintMidY
relativeTo: @"superlayer"
attribute: kCAConstraintMaxY
scale: (r + 0.5) / (float)rows
offset: 0]];
Dunk said
I wish iPhone had CALayer’s….
(I can freely say that now since the NDA is gone!)
Dunk said
Oh, no wait – iPhone DOES have CALayers
(I can say that too!).
Phage v1.0 teaser « Hot Chocolate said
[...] elements of the game. It took some time before I managed to wrap my head around how to make a re-sizeable grid of CALayer instances, but once I finally did the rest has been pretty smooth sailing. It’s really satisfying [...]
Mike said
Thanks — just what I was looking for!
Core Animation CAConstraint Grid-Cell Layout | Mozketo said
[...] a little digging around I was able to find an amazing Hot Chocolate blog post which showed a fantastic trick when laying out the Cells without having to reference the prior [...]
Alex said
Thanx for this example!
It’s helpfull to understand CALayers layout managment.
Now i think: how to animate dragging cells between it’s places?..
Adrian said
Great post! helped me a lot. How could you adapt this so you had a Finder-like thumbnail view? when i resize the window it resizes all the cells.