對多個視圖進行左對齊或者右對齊的排版,有幾種實現方式。數組
好比能夠根據視圖容器的寬度,而後逐行計算此行能夠放下幾個視圖,超過換行。這種方式須要複雜的計算,容易出錯。lua
下面,介紹一種比較簡易的實現方式,即是使用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; }