UICollection View Flow Layout Vertical Align UICollection View Flow Layout Vertical Align ios ios

UICollection View Flow Layout Vertical Align


Swift 4 with functional oriented approach:

class TopAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {        let attributes = super.layoutAttributesForElements(in: rect)?            .map { $0.copy() } as? [UICollectionViewLayoutAttributes]    attributes?        .reduce([CGFloat: (CGFloat, [UICollectionViewLayoutAttributes])]()) {            guard $1.representedElementCategory == .cell else { return $0 }            return $0.merging([ceil($1.center.y): ($1.frame.origin.y, [$1])]) {                ($0.0 < $1.0 ? $0.0 : $1.0, $0.1 + $1.1)            }        }        .values.forEach { minY, line in            line.forEach {                $0.frame = $0.frame.offsetBy(                    dx: 0,                    dy: minY - $0.frame.origin.y                )            }        }        return attributes    }}


following code worked for me

@interface TopAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout- (void)alignToTopForSameLineElements:(NSArray *)sameLineElements;@end@implementation TopAlignedCollectionViewFlowLayout- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;{    NSArray *attrs = [super layoutAttributesForElementsInRect:rect];    CGFloat baseline = -2;    NSMutableArray *sameLineElements = [NSMutableArray array];    for (UICollectionViewLayoutAttributes *element in attrs) {        if (element.representedElementCategory == UICollectionElementCategoryCell) {            CGRect frame = element.frame;            CGFloat centerY = CGRectGetMidY(frame);            if (ABS(centerY - baseline) > 1) {                baseline = centerY;                [self alignToTopForSameLineElements:sameLineElements];                [sameLineElements removeAllObjects];            }            [sameLineElements addObject:element];        }    }    [self alignToTopForSameLineElements:sameLineElements];//align one more time for the last line    return attrs;}- (void)alignToTopForSameLineElements:(NSArray *)sameLineElements{    if (sameLineElements.count == 0) {        return;    }    NSArray *sorted = [sameLineElements sortedArrayUsingComparator:^NSComparisonResult(UICollectionViewLayoutAttributes *obj1, UICollectionViewLayoutAttributes *obj2) {        CGFloat height1 = obj1.frame.size.height;        CGFloat height2 = obj2.frame.size.height;        CGFloat delta = height1 - height2;        return delta == 0. ? NSOrderedSame : ABS(delta)/delta;    }];    UICollectionViewLayoutAttributes *tallest = [sorted lastObject];    [sameLineElements enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL *stop) {        obj.frame = CGRectOffset(obj.frame, 0, tallest.frame.origin.y - obj.frame.origin.y);    }];}@end


@DongXu: Your solution worked for me too. Here is the SWIFT version if it:

class TopAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout{    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?    {        if let attrs = super.layoutAttributesForElementsInRect(rect)        {            var baseline: CGFloat = -2            var sameLineElements = [UICollectionViewLayoutAttributes]()            for element in attrs            {                if element.representedElementCategory == .Cell                {                    let frame = element.frame                    let centerY = CGRectGetMidY(frame)                    if abs(centerY - baseline) > 1                    {                        baseline = centerY                        TopAlignedCollectionViewFlowLayout.alignToTopForSameLineElements(sameLineElements)                        sameLineElements.removeAll()                    }                    sameLineElements.append(element)                }            }            TopAlignedCollectionViewFlowLayout.alignToTopForSameLineElements(sameLineElements) // align one more time for the last line            return attrs        }        return nil    }    private class func alignToTopForSameLineElements(sameLineElements: [UICollectionViewLayoutAttributes])    {        if sameLineElements.count < 1        {            return        }        let sorted = sameLineElements.sort { (obj1: UICollectionViewLayoutAttributes, obj2: UICollectionViewLayoutAttributes) -> Bool in            let height1 = obj1.frame.size.height            let height2 = obj2.frame.size.height            let delta = height1 - height2            return delta <= 0        }        if let tallest = sorted.last        {            for obj in sameLineElements            {                obj.frame = CGRectOffset(obj.frame, 0, tallest.frame.origin.y - obj.frame.origin.y)            }        }    }}