Skip to content

Instantly share code, notes, and snippets.

@WenchaoD
Last active May 12, 2020 03:40
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save WenchaoD/06740053c8c0ddd3dc70 to your computer and use it in GitHub Desktop.
Save WenchaoD/06740053c8c0ddd3dc70 to your computer and use it in GitHub Desktop.
Subclass UICollectionViewFlowLayout to build a tag layout, and make UICollectionView scrolling smonthly while it holds dozens of cells with round corner
//
// TagCollectionViewCell.h
//
// Created by DingWenchao on 7/14/15.
// Copyright (c) 2015 demo. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TagCollectionViewCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UILabel *textLabel;
@end
#import "TagCollectionViewCell.h"
@implementation TagCollectionViewCell
// Trick about round corner. If you set _textLabel.layer.cornerRadius = [],
// the scrolling speed of collectionView would suck.
- (void)awakeFromNib
{
_textLabel.backgroundColor = [UIColor clearColor];
_textLabel.layer.cornerRadius = 5;
// Huge change in performance by explicitly setting the below (even though default is supposedly NO)
_textLabel.layer.masksToBounds = NO;
// Performance improvement here depends on the size of your view
_textLabel.layer.shouldRasterize = YES;
_textLabel.layer.backgroundColor = _invert ? kRedTagColor.CGColor : kGreyTagColor.CGColor;
_textLabel.layer.rasterizationScale = [UIScreen mainScreen].scale;
}
@end
#import <UIKist/UIKit.h>
@interface TagFlowLayout : UICollectionViewFlowLayout
@end
#import "TagFlowLayout.h"
const NSInteger kMaxCellSpacing = 10;
@implementation TagFlowLayout
- (instancetype)init
{
self = [super init];
if (self) {
self.scrollDirection = UICollectionViewScrollDirectionVertical;
self.minimumInteritemSpacing = kMaxCellSpacing;
self.minimumLineSpacing = kMaxCellSpacing;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.scrollDirection = UICollectionViewScrollDirectionVertical;
self.minimumInteritemSpacing = kMaxCellSpacing;
self.minimumLineSpacing = kMaxCellSpacing;
}
return self;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
if (nil == attributes.representedElementKind) {
NSIndexPath* indexPath = attributes.indexPath;
attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
}
}
return attributesToReturn;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes* currentItemAttributes =
[super layoutAttributesForItemAtIndexPath:indexPath];
UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout sectionInset];
if (indexPath.item == 0) { // first item of section
CGRect frame = currentItemAttributes.frame;
frame.origin.x = sectionInset.left; // first item of the section should always be left aligned
currentItemAttributes.frame = frame;
return currentItemAttributes;
}
NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];
CGRect previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame;
CGFloat previousFrameRightPoint = previousFrame.origin.x + previousFrame.size.width + kMaxCellSpacing;
CGRect currentFrame = currentItemAttributes.frame;
CGRect strecthedCurrentFrame = CGRectMake(0,
currentFrame.origin.y,
self.collectionView.frame.size.width,
currentFrame.size.height);
if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) { // if current item is the first item on the line
// the approach here is to take the current frame, left align it to the edge of the view
// then stretch it the width of the collection view, if it intersects with the previous frame then that means it
// is on the same line, otherwise it is on it's own new line
CGRect frame = currentItemAttributes.frame;
frame.origin.x = sectionInset.left; // first item on the line should always be left aligned
currentItemAttributes.frame = frame;
return currentItemAttributes;
}
CGRect frame = currentItemAttributes.frame;
frame.origin.x = previousFrameRightPoint;
currentItemAttributes.frame = frame;
return currentItemAttributes;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment