PSTGridLayoutSection.m 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //
  2. // PSTGridLayoutSection.m
  3. // PSPDFKit
  4. //
  5. // Copyright (c) 2012-2013 Peter Steinberger. All rights reserved.
  6. //
  7. #import "PSTGridLayoutSection.h"
  8. #import "PSTGridLayoutItem.h"
  9. #import "PSTGridLayoutRow.h"
  10. #import "PSTGridLayoutInfo.h"
  11. @interface PSTGridLayoutSection () {
  12. NSMutableArray *_items;
  13. NSMutableArray *_rows;
  14. BOOL _isValid;
  15. }
  16. @property (nonatomic, strong) NSArray *items;
  17. @property (nonatomic, strong) NSArray *rows;
  18. @property (nonatomic, assign) CGFloat otherMargin;
  19. @property (nonatomic, assign) CGFloat beginMargin;
  20. @property (nonatomic, assign) CGFloat endMargin;
  21. @property (nonatomic, assign) CGFloat actualGap;
  22. @property (nonatomic, assign) CGFloat lastRowBeginMargin;
  23. @property (nonatomic, assign) CGFloat lastRowEndMargin;
  24. @property (nonatomic, assign) CGFloat lastRowActualGap;
  25. @property (nonatomic, assign) BOOL lastRowIncomplete;
  26. @property (nonatomic, assign) NSInteger itemsByRowCount;
  27. @property (nonatomic, assign) NSInteger indexOfImcompleteRow;
  28. @end
  29. @implementation PSTGridLayoutSection
  30. ///////////////////////////////////////////////////////////////////////////////////////////
  31. #pragma mark - NSObject
  32. - (id)init {
  33. if ((self = [super init])) {
  34. _items = [NSMutableArray new];
  35. _rows = [NSMutableArray new];
  36. }
  37. return self;
  38. }
  39. - (NSString *)description {
  40. return [NSString stringWithFormat:@"<%@: %p itemCount:%ld frame:%@ rows:%@>", NSStringFromClass(self.class), self, (long)self.itemsCount, NSStringFromCGRect(self.frame), self.rows];
  41. }
  42. ///////////////////////////////////////////////////////////////////////////////////////////
  43. #pragma mark - Public
  44. - (void)invalidate {
  45. _isValid = NO;
  46. self.rows = [NSMutableArray array];
  47. }
  48. - (void)computeLayout {
  49. if (!_isValid) {
  50. NSAssert(self.rows.count == 0, @"No rows shall be at this point.");
  51. // iterate over all items, turning them into rows.
  52. CGSize sectionSize = CGSizeZero;
  53. NSInteger rowIndex = 0;
  54. NSInteger itemIndex = 0;
  55. NSInteger itemsByRowCount = 0;
  56. CGFloat dimensionLeft = 0;
  57. PSTGridLayoutRow *row = nil;
  58. // get dimension and compensate for section margin
  59. CGFloat headerFooterDimension = self.layoutInfo.dimension;
  60. CGFloat dimension = headerFooterDimension;
  61. if (self.layoutInfo.horizontal) {
  62. dimension -= self.sectionMargins.top + self.sectionMargins.bottom;
  63. self.headerFrame = CGRectMake(sectionSize.width, 0, self.headerDimension, headerFooterDimension);
  64. sectionSize.width += self.headerDimension + self.sectionMargins.left;
  65. }else {
  66. dimension -= self.sectionMargins.left + self.sectionMargins.right;
  67. self.headerFrame = CGRectMake(0, sectionSize.height, headerFooterDimension, self.headerDimension);
  68. sectionSize.height += self.headerDimension + self.sectionMargins.top;
  69. }
  70. CGFloat spacing = self.layoutInfo.horizontal ? self.verticalInterstice : self.horizontalInterstice;
  71. do {
  72. BOOL finishCycle = itemIndex >= self.itemsCount;
  73. // TODO: fast path could even remove row creation and just calculate on the fly
  74. PSTGridLayoutItem *item = nil;
  75. if (!finishCycle) item = self.fixedItemSize ? nil : self.items[(NSUInteger)itemIndex];
  76. CGSize itemSize = self.fixedItemSize ? self.itemSize : item.itemFrame.size;
  77. CGFloat itemDimension = self.layoutInfo.horizontal ? itemSize.height : itemSize.width;
  78. // first item of each row does not add spacing
  79. if (itemsByRowCount > 0) itemDimension += spacing;
  80. if (dimensionLeft < itemDimension || finishCycle) {
  81. // finish current row
  82. if (row) {
  83. // compensate last row
  84. self.itemsByRowCount = fmax(itemsByRowCount, self.itemsByRowCount);
  85. row.itemCount = itemsByRowCount;
  86. // if current row is done but there are still items left, increase the incomplete row counter
  87. if (!finishCycle) self.indexOfImcompleteRow = rowIndex;
  88. [row layoutRow];
  89. if (self.layoutInfo.horizontal) {
  90. row.rowFrame = CGRectMake(sectionSize.width, self.sectionMargins.top, row.rowSize.width, row.rowSize.height);
  91. sectionSize.height = MAX(row.rowSize.height, sectionSize.height);
  92. sectionSize.width += row.rowSize.width + (finishCycle ? 0 : self.horizontalInterstice);
  93. }else {
  94. row.rowFrame = CGRectMake(self.sectionMargins.left, sectionSize.height, row.rowSize.width, row.rowSize.height);
  95. sectionSize.height += row.rowSize.height + (finishCycle ? 0 : self.verticalInterstice);
  96. sectionSize.width = MAX(row.rowSize.width, sectionSize.width);
  97. }
  98. }
  99. // add new rows until the section is fully laid out
  100. if (!finishCycle) {
  101. // create new row
  102. row.complete = YES; // finish up current row
  103. row = [self addRow];
  104. row.fixedItemSize = self.fixedItemSize;
  105. row.index = rowIndex;
  106. self.indexOfImcompleteRow = rowIndex;
  107. rowIndex++;
  108. // convert an item from previous row to current, remove spacing for first item
  109. if (itemsByRowCount > 0) itemDimension -= spacing;
  110. dimensionLeft = dimension - itemDimension;
  111. itemsByRowCount = 0;
  112. }
  113. }else {
  114. dimensionLeft -= itemDimension;
  115. }
  116. // add item on slow path
  117. if (item) [row addItem:item];
  118. itemIndex++;
  119. itemsByRowCount++;
  120. } while (itemIndex <= self.itemsCount); // cycle once more to finish last row
  121. if (self.layoutInfo.horizontal) {
  122. sectionSize.width += self.sectionMargins.right;
  123. self.footerFrame = CGRectMake(sectionSize.width, 0, self.footerDimension, headerFooterDimension);
  124. sectionSize.width += self.footerDimension;
  125. }else {
  126. sectionSize.height += self.sectionMargins.bottom;
  127. self.footerFrame = CGRectMake(0, sectionSize.height, headerFooterDimension, self.footerDimension);
  128. sectionSize.height += self.footerDimension;
  129. }
  130. _frame = CGRectMake(0, 0, sectionSize.width, sectionSize.height);
  131. _isValid = YES;
  132. }
  133. }
  134. - (void)recomputeFromIndex:(NSInteger)index {
  135. // TODO: use index.
  136. [self invalidate];
  137. [self computeLayout];
  138. }
  139. - (PSTGridLayoutItem *)addItem {
  140. PSTGridLayoutItem *item = [PSTGridLayoutItem new];
  141. item.section = self;
  142. [_items addObject:item];
  143. return item;
  144. }
  145. - (PSTGridLayoutRow *)addRow {
  146. PSTGridLayoutRow *row = [PSTGridLayoutRow new];
  147. row.section = self;
  148. [_rows addObject:row];
  149. return row;
  150. }
  151. - (PSTGridLayoutSection *)snapshot {
  152. PSTGridLayoutSection *snapshotSection = [PSTGridLayoutSection new];
  153. snapshotSection.items = [self.items copy];
  154. snapshotSection.rows = [self.items copy];
  155. snapshotSection.verticalInterstice = self.verticalInterstice;
  156. snapshotSection.horizontalInterstice = self.horizontalInterstice;
  157. snapshotSection.sectionMargins = self.sectionMargins;
  158. snapshotSection.frame = self.frame;
  159. snapshotSection.headerFrame = self.headerFrame;
  160. snapshotSection.footerFrame = self.footerFrame;
  161. snapshotSection.headerDimension = self.headerDimension;
  162. snapshotSection.footerDimension = self.footerDimension;
  163. snapshotSection.layoutInfo = self.layoutInfo;
  164. snapshotSection.rowAlignmentOptions = self.rowAlignmentOptions;
  165. snapshotSection.fixedItemSize = self.fixedItemSize;
  166. snapshotSection.itemSize = self.itemSize;
  167. snapshotSection.itemsCount = self.itemsCount;
  168. snapshotSection.otherMargin = self.otherMargin;
  169. snapshotSection.beginMargin = self.beginMargin;
  170. snapshotSection.endMargin = self.endMargin;
  171. snapshotSection.actualGap = self.actualGap;
  172. snapshotSection.lastRowBeginMargin = self.lastRowBeginMargin;
  173. snapshotSection.lastRowEndMargin = self.lastRowEndMargin;
  174. snapshotSection.lastRowActualGap = self.lastRowActualGap;
  175. snapshotSection.lastRowIncomplete = self.lastRowIncomplete;
  176. snapshotSection.itemsByRowCount = self.itemsByRowCount;
  177. snapshotSection.indexOfImcompleteRow = self.indexOfImcompleteRow;
  178. return snapshotSection;
  179. }
  180. - (NSInteger)itemsCount {
  181. return self.fixedItemSize ? _itemsCount : (NSInteger)self.items.count;
  182. }
  183. @end