123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- //
- // PSTGridLayoutRow.m
- // PSPDFKit
- //
- // Copyright (c) 2012-2013 Peter Steinberger. All rights reserved.
- //
- #import "PSTCollectionView.h"
- #import "PSTGridLayoutRow.h"
- #import "PSTGridLayoutSection.h"
- #import "PSTGridLayoutItem.h"
- #import "PSTGridLayoutInfo.h"
- @interface PSTGridLayoutRow () {
- NSMutableArray *_items;
- BOOL _isValid;
- int _verticalAlignement;
- int _horizontalAlignement;
- }
- @property (nonatomic, strong) NSArray *items;
- @end
- @implementation PSTGridLayoutRow
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - NSObject
- - (id)init {
- if ((self = [super init])) {
- _items = [NSMutableArray new];
- }
- return self;
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p frame:%@ index:%ld items:%@>", NSStringFromClass(self.class), self, NSStringFromCGRect(self.rowFrame), (long)self.index, self.items];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark - Public
- - (void)invalidate {
- _isValid = NO;
- _rowSize = CGSizeZero;
- _rowFrame = CGRectZero;
- }
- - (NSArray *)itemRects {
- return [self layoutRowAndGenerateRectArray:YES];
- }
- - (void)layoutRow {
- [self layoutRowAndGenerateRectArray:NO];
- }
- - (NSArray *)layoutRowAndGenerateRectArray:(BOOL)generateRectArray {
- NSMutableArray *rects = generateRectArray ? [NSMutableArray array] : nil;
- if (!_isValid || generateRectArray) {
- // properties for aligning
- BOOL isHorizontal = self.section.layoutInfo.horizontal;
- BOOL isLastRow = self.section.indexOfImcompleteRow == self.index;
- PSTFlowLayoutHorizontalAlignment horizontalAlignment = [self.section.rowAlignmentOptions[isLastRow ? PSTFlowLayoutLastRowHorizontalAlignmentKey : PSTFlowLayoutCommonRowHorizontalAlignmentKey] integerValue];
- // calculate space that's left over if we would align it from left to right.
- CGFloat leftOverSpace = self.section.layoutInfo.dimension;
- if (isHorizontal) {
- leftOverSpace -= self.section.sectionMargins.top + self.section.sectionMargins.bottom;
- }else {
- leftOverSpace -= self.section.sectionMargins.left + self.section.sectionMargins.right;
- }
- // calculate the space that we have left after counting all items.
- // UICollectionView is smart and lays out items like they would have been placed on a full row
- // So we need to calculate the "usedItemCount" with using the last item as a reference size.
- // This allows us to correctly justify-place the items in the grid.
- NSInteger usedItemCount = 0;
- NSInteger itemIndex = 0;
- CGFloat spacing = isHorizontal ? self.section.verticalInterstice : self.section.horizontalInterstice;
- // the last row should justify as if it is filled with more (invisible) items so that the whole
- // UICollectionView feels more like a grid than a random line of blocks
- while (itemIndex < self.itemCount || isLastRow) {
- CGFloat nextItemSize;
- // first we need to find the size (width/height) of the next item to fit
- if (!self.fixedItemSize) {
- PSTGridLayoutItem *item = self.items[(NSUInteger)MIN(itemIndex, self.itemCount - 1)];
- nextItemSize = isHorizontal ? item.itemFrame.size.height : item.itemFrame.size.width;
- }else {
- nextItemSize = isHorizontal ? self.section.itemSize.height : self.section.itemSize.width;
- }
- // the first item does not add a separator spacing,
- // every one afterwards in the same row will need this spacing constant
- if (itemIndex > 0) {
- nextItemSize += spacing;
- }
- // check to see if we can at least fit an item (+separator if necessary)
- if (leftOverSpace < nextItemSize) {
- break;
- }
- // we need to maintain the leftover space after the maximum amount of items have
- // occupied, so we know how to adjust equal spacing among all the items in a row
- leftOverSpace -= nextItemSize;
- itemIndex++;
- usedItemCount = itemIndex;
- }
- // push everything to the right if right-aligning and divide in half for centered
- // currently there is no public API supporting this behavior
- CGPoint itemOffset = CGPointZero;
- if (horizontalAlignment == PSTFlowLayoutHorizontalAlignmentRight) {
- itemOffset.x += leftOverSpace;
- }else if (horizontalAlignment == PSTFlowLayoutHorizontalAlignmentCentered ||
- (horizontalAlignment == PSTFlowLayoutHorizontalAlignmentJustify && usedItemCount == 1)) {
- // Special case one item row to split leftover space in half
- itemOffset.x += leftOverSpace / 2;
- }
- // calculate the justified spacing among all items in a row if we are using
- // the default PSTFlowLayoutHorizontalAlignmentJustify layout
- CGFloat interSpacing = usedItemCount <= 1 ? 0 : leftOverSpace / (CGFloat)(usedItemCount - 1);
- // calculate row frame as union of all items
- CGRect frame = CGRectZero;
- CGRect itemFrame = (CGRect){.size=self.section.itemSize};
- for (itemIndex = 0; itemIndex < self.itemCount; itemIndex++) {
- PSTGridLayoutItem *item = nil;
- if (!self.fixedItemSize) {
- item = self.items[(NSUInteger)itemIndex];
- itemFrame = [item itemFrame];
- }
- // depending on horizontal/vertical for an item size (height/width),
- // we add the minimum separator then an equally distributed spacing
- // (since our default mode is justify) calculated from the total leftover
- // space divided by the number of intervals
- if (isHorizontal) {
- itemFrame.origin.y = itemOffset.y;
- itemOffset.y += itemFrame.size.height + self.section.verticalInterstice;
- if (horizontalAlignment == PSTFlowLayoutHorizontalAlignmentJustify) {
- itemOffset.y += interSpacing;
- }
- }else {
- itemFrame.origin.x = itemOffset.x;
- itemOffset.x += itemFrame.size.width + self.section.horizontalInterstice;
- if (horizontalAlignment == PSTFlowLayoutHorizontalAlignmentJustify) {
- itemOffset.x += interSpacing;
- }
- }
- item.itemFrame = itemFrame; // might call nil; don't care
- [rects addObject:[NSValue valueWithCGRect:itemFrame]];
- frame = CGRectUnion(frame, itemFrame);
- }
- _rowSize = frame.size;
- // _rowFrame = frame; // set externally
- _isValid = YES;
- }
- return rects;
- }
- - (void)addItem:(PSTGridLayoutItem *)item {
- [_items addObject:item];
- item.rowObject = self;
- [self invalidate];
- }
- - (PSTGridLayoutRow *)snapshot {
- PSTGridLayoutRow *snapshotRow = [self.class new];
- snapshotRow.section = self.section;
- snapshotRow.items = self.items;
- snapshotRow.rowSize = self.rowSize;
- snapshotRow.rowFrame = self.rowFrame;
- snapshotRow.index = self.index;
- snapshotRow.complete = self.complete;
- snapshotRow.fixedItemSize = self.fixedItemSize;
- snapshotRow.itemCount = self.itemCount;
- return snapshotRow;
- }
- - (PSTGridLayoutRow *)copyFromSection:(PSTGridLayoutSection *)section {
- return nil; // ???
- }
- - (NSInteger)itemCount {
- if (self.fixedItemSize) {
- return _itemCount;
- }else {
- return (NSInteger)self.items.count;
- }
- }
- @end
|