Reputation: 17861
I'm using UICollectionView to lay out a bunch of cells that are sectioned by first letter of their title. Each cell should have a very thin border around it, and the section headers should have borders above and below. Here's my current prototype:
I achieve the current appearance with the following rules:
This is very close to what I want, but there are two defects:
My best idea to fix this is to somehow tell each cell if it's in the last row of a section or the last cell in a row; then the cell would turn off the offending borders, section headings would draw a top border as well as a bottom, and everything would be hunky-dory. I don't know how to achieve that, though.
Any thoughts on how to manage that, or another way to get the look I'm going for? I'm currently using a UICollectionViewFlowLayout.
Upvotes: 4
Views: 11375
Reputation: 513
I solve this problem in a simple way. I didn't add boarder to cell, instead I add a label with boarder into the cell. For the first column, the frame of the label is the same with the cell. For the other label, I set the x coordinate -0.5 to make their boarder overlap. Hope it helps.
Here is the code:
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
// Then use it
UILabel *label = nil;
if (cell.contentView.subviews.count > 0) {
label = cell.contentView.subviews[0];
} else {
label = [[UILabel alloc] init];
}
label.text = @"北京";
[label setTextAlignment:NSTextAlignmentCenter];
[label setFont:[UIFont systemFontOfSize:14]];
[label setCenter:cell.contentView.center];
CGRect frame = label.frame;
if (indexPath.row%4 == 0) {
frame.origin.x = 0;
} else {
frame.origin.x = -0.5;
}
frame.origin.y = 0;
frame.size.width = self.collectionView.frame.size.width / 4;
frame.size.height = self.collectionView.frame.size.height / 9;
[label setFrame:frame];
if (cell.contentView.subviews.count == 0) {
[[cell contentView] addSubview:label];
}
label.layer.borderWidth = 0.5;
label.layer.borderColor = [[UIColor lightGrayColor] CGColor];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
Upvotes: 3
Reputation: 17861
I ended up subclassing UICollectionViewFlowLayout
and applying several heuristics after the flow layout had calculated the attributes for each cell:
center.y
is equal to center.y
of the last item in the section, the cell is in the last row of the section.CGRectGetMaxY(frame)
is equal to CGRectGetMaxY(self.collectionView.bounds)
, then the cell is agains the right edge of the collection view.I then stored the results of these calculations in a subclass of UICollectionViewLayoutAttributes
, and wrote a UICollectionViewCell
subclass whose -applyLayoutAttributes:
method would adjust the borders its background view draws based on the additional properties.
I've put the whole mess into a fairly enormous gist so you can see exactly what I did. Happy hacking.
Upvotes: 4
Reputation: 9915
My best idea to fix this is to somehow tell each cell if it's in the last row of a section or the last cell in a row; then the cell would turn off the offending borders, section headings would draw a top border as well as a bottom, and everything would be hunky-dory. I don't know how to achieve that, though.
What you describe is more or less what I did in a similar scenario. I added a border property to my cell:
typedef NS_OPTIONS(NSInteger, TLGridBorder) {
TLGridBorderNone = 0,
TLGridBorderTop = 1 << 0,
TLGridBorderRight = 1 << 1,
TLGridBorderBottom = 1 << 2,
TLGridBorderLeft = 1 << 3,
TLGridBorderAll = TLGridBorderTop | TLGridBorderRight | TLGridBorderBottom | TLGridBorderLeft,
};
@interface TLGridCellView : UIView
@property (nonatomic) TLGridBorder border;
@end
Then I set the border in my view controller's cell configuration:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TLGridCellView *cell = ...;
if (indexPath.item == self collectionView:collectionView numberOfItemsInSection:indexPath.section - 1) {
cell.border = TLGridBorderLeft;
} else {
cell.border = TLGridBorderLeft | TLGridBorderRight;
}
return cell;
}
Upvotes: 3