使用UICollectionView简单实现多个视图的左对齐和右对齐排版

使用UICollectionView简单实现多个视图的左对齐和右对齐排版

一般作法

对多个视图进行左对齐或者右对齐的排版,有几种实现方式。数组

好比能够根据视图容器的宽度,而后逐行计算此行能够放下几个视图,超过换行。这种方式须要复杂的计算,容易出错。lua

使用UIColletionView的UICollectionViewFlowLayout

下面,介绍一种比较简易的实现方式,即是使用UICollectionView。设计

具体则是在 UICollectionViewFlowLayout 的基础上,设计子类,而后重写如下方法code

- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect; // return an array layout attributes instances for all the views in the given rect

原理

layoutAttributesForElementsInRect 方法里,能够拿到UIColletionView的默认排版下的全部视图的数组,此数组存储了 UICollectionViewLayoutAttributes 对象,对应每一个项的详情信息,包括已经排版完成的frame,其所在的indexPath等。对象

拿到数组后,在左对齐的状况下,则顺序遍历,而后从新设置每一个项的x轴位置,则完成了排版。ci

一样,在右对齐的状况下,则倒序遍历数组,从新设置每一个项的x轴位置,完成排版。it

具体代码

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *originalAttributes = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray *updatedAttributes = [NSMutableArray arrayWithArray:originalAttributes];
    
    if (self.rightAligned) {
        // right aligned
        for (NSInteger index = originalAttributes.count - 1; index >= 0; index--) {
            UICollectionViewLayoutAttributes * attributes = originalAttributes[index];
            if (!attributes.representedElementKind) {
                NSUInteger index = [updatedAttributes indexOfObject:attributes];
                updatedAttributes[index] = [self layoutAttributesForItemRightAlignedAtIndexPath:attributes.indexPath itemCount:originalAttributes.count];
            }
        }
    } else {
        // left aligned
        for (UICollectionViewLayoutAttributes *attributes in originalAttributes) {
            if (!attributes.representedElementKind) {
                NSUInteger index = [updatedAttributes indexOfObject:attributes];
                updatedAttributes[index] = [self layoutAttributesForItemAtIndexPath:attributes.indexPath];
            }
        }
    }
    
    return updatedAttributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemRightAlignedAtIndexPath:(NSIndexPath *)indexPath itemCount:(NSInteger)itemCount
{
    UICollectionViewLayoutAttributes* currentItemAttributes = [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
    UIEdgeInsets sectionInset = [self evaluatedSectionInsetForItemAtIndex:indexPath.section];
    
    BOOL isLastItemInSection = indexPath.item == itemCount - 1;
    if (isLastItemInSection) {
        [currentItemAttributes rightAlignFrameWithSectionInset:sectionInset collectionViewWidth:self.collectionView.frame.size.width];
        return currentItemAttributes;
    }
    
    // the total width without left inset and right inset
    CGFloat layoutWidth = CGRectGetWidth(self.collectionView.frame) - sectionInset.left - sectionInset.right;
    
    // reversed cycle
    NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item+1 inSection:indexPath.section];
    CGRect previousFrame = [self layoutAttributesForItemRightAlignedAtIndexPath:previousIndexPath itemCount:itemCount].frame;
    CGFloat previousFrameLeftPoint = previousFrame.origin.x;
    CGRect currentFrame = currentItemAttributes.frame;
    CGRect strecthedCurrentFrame = CGRectMake(sectionInset.left,
                                              currentFrame.origin.y,
                                              layoutWidth,
                                              currentFrame.size.height);
    // if the current frame, once left aligned to the left and stretched to the full collection view
    // widht intersects the previous frame then they are on the same line
    BOOL isLastItemInRow = !CGRectIntersectsRect(previousFrame, strecthedCurrentFrame);

    if (isLastItemInRow) {
        // make sure the last item on a line is right aligned
        [currentItemAttributes rightAlignFrameWithSectionInset:self.sectionInset collectionViewWidth:self.collectionView.frame.size.width];
        return currentItemAttributes;
    }
    
    // reset the frame of current item
    CGRect frame = currentItemAttributes.frame;
    frame.origin.x = previousFrameLeftPoint - [self evaluatedMinimumInteritemSpacingForSectionAtIndex:indexPath.section] - frame.size.width;
    currentItemAttributes.frame = frame;
    
    return currentItemAttributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes* currentItemAttributes = [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
    UIEdgeInsets sectionInset = [self evaluatedSectionInsetForItemAtIndex:indexPath.section];
    
    BOOL isFirstItemInSection = indexPath.item == 0;
    CGFloat layoutWidth = CGRectGetWidth(self.collectionView.frame) - sectionInset.left - sectionInset.right;
    
    if (isFirstItemInSection) {
        [currentItemAttributes leftAlignFrameWithSectionInset:sectionInset];
        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;
    CGRect currentFrame = currentItemAttributes.frame;
    CGRect strecthedCurrentFrame = CGRectMake(sectionInset.left,
                                              currentFrame.origin.y,
                                              layoutWidth,
                                              currentFrame.size.height);
    // if the current frame, once left aligned to the left and stretched to the full collection view
    // widht intersects the previous frame then they are on the same line
    BOOL isFirstItemInRow = !CGRectIntersectsRect(previousFrame, strecthedCurrentFrame);
    
    if (isFirstItemInRow) {
        // make sure the first item on a line is left aligned
        [currentItemAttributes leftAlignFrameWithSectionInset:sectionInset];
        return currentItemAttributes;
    }
    
    CGRect frame = currentItemAttributes.frame;
    frame.origin.x = previousFrameRightPoint + [self evaluatedMinimumInteritemSpacingForSectionAtIndex:indexPath.section];
    currentItemAttributes.frame = frame;

    return currentItemAttributes;
}
相关文章
相关标签/搜索