CHTCollectionViewWaterfallLayout.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. //
  2. // UICollectionViewWaterfallLayout.m
  3. //
  4. // Created by Nelson on 12/11/19.
  5. // Copyright (c) 2012 Nelson Tai. All rights reserved.
  6. //
  7. #import "CHTCollectionViewWaterfallLayout.h"
  8. #import "tgmath.h"
  9. NSString *const CHTCollectionElementKindSectionHeader = @"CHTCollectionElementKindSectionHeader";
  10. NSString *const CHTCollectionElementKindSectionFooter = @"CHTCollectionElementKindSectionFooter";
  11. @interface CHTCollectionViewWaterfallLayout ()
  12. /// The delegate will point to collection view's delegate automatically.
  13. @property (nonatomic, weak) id <CHTCollectionViewDelegateWaterfallLayout> delegate;
  14. /// Array to store height for each column
  15. @property (nonatomic, strong) NSMutableArray *columnHeights;
  16. /// Array of arrays. Each array stores item attributes for each section
  17. @property (nonatomic, strong) NSMutableArray *sectionItemAttributes;
  18. /// Array to store attributes for all items includes headers, cells, and footers
  19. @property (nonatomic, strong) NSMutableArray *allItemAttributes;
  20. /// Dictionary to store section headers' attribute
  21. @property (nonatomic, strong) NSMutableDictionary *headersAttribute;
  22. /// Dictionary to store section footers' attribute
  23. @property (nonatomic, strong) NSMutableDictionary *footersAttribute;
  24. /// Array to store union rectangles
  25. @property (nonatomic, strong) NSMutableArray *unionRects;
  26. @end
  27. @implementation CHTCollectionViewWaterfallLayout
  28. /// How many items to be union into a single rectangle
  29. static const NSInteger unionSize = 20;
  30. static CGFloat CHTFloorCGFloat(CGFloat value) {
  31. CGFloat scale = [UIScreen mainScreen].scale;
  32. return floor(value * scale) / scale;
  33. }
  34. #pragma mark - Public Accessors
  35. - (void)setColumnCount:(NSInteger)columnCount {
  36. if (_columnCount != columnCount) {
  37. _columnCount = columnCount;
  38. [self invalidateLayout];
  39. }
  40. }
  41. - (void)setMinimumColumnSpacing:(CGFloat)minimumColumnSpacing {
  42. if (_minimumColumnSpacing != minimumColumnSpacing) {
  43. _minimumColumnSpacing = minimumColumnSpacing;
  44. [self invalidateLayout];
  45. }
  46. }
  47. - (void)setMinimumInteritemSpacing:(CGFloat)minimumInteritemSpacing {
  48. if (_minimumInteritemSpacing != minimumInteritemSpacing) {
  49. _minimumInteritemSpacing = minimumInteritemSpacing;
  50. [self invalidateLayout];
  51. }
  52. }
  53. - (void)setHeaderHeight:(CGFloat)headerHeight {
  54. if (_headerHeight != headerHeight) {
  55. _headerHeight = headerHeight;
  56. [self invalidateLayout];
  57. }
  58. }
  59. - (void)setFooterHeight:(CGFloat)footerHeight {
  60. if (_footerHeight != footerHeight) {
  61. _footerHeight = footerHeight;
  62. [self invalidateLayout];
  63. }
  64. }
  65. - (void)setHeaderInset:(UIEdgeInsets)headerInset {
  66. if (!UIEdgeInsetsEqualToEdgeInsets(_headerInset, headerInset)) {
  67. _headerInset = headerInset;
  68. [self invalidateLayout];
  69. }
  70. }
  71. - (void)setFooterInset:(UIEdgeInsets)footerInset {
  72. if (!UIEdgeInsetsEqualToEdgeInsets(_footerInset, footerInset)) {
  73. _footerInset = footerInset;
  74. [self invalidateLayout];
  75. }
  76. }
  77. - (void)setSectionInset:(UIEdgeInsets)sectionInset {
  78. if (!UIEdgeInsetsEqualToEdgeInsets(_sectionInset, sectionInset)) {
  79. _sectionInset = sectionInset;
  80. [self invalidateLayout];
  81. }
  82. }
  83. - (void)setItemRenderDirection:(CHTCollectionViewWaterfallLayoutItemRenderDirection)itemRenderDirection {
  84. if (_itemRenderDirection != itemRenderDirection) {
  85. _itemRenderDirection = itemRenderDirection;
  86. [self invalidateLayout];
  87. }
  88. }
  89. - (NSInteger)columnCountForSection:(NSInteger)section {
  90. if ([self.delegate respondsToSelector:@selector(collectionView:layout:columnCountForSection:)]) {
  91. return [self.delegate collectionView:self.collectionView layout:self columnCountForSection:section];
  92. } else {
  93. return self.columnCount;
  94. }
  95. }
  96. - (CGFloat)itemWidthInSectionAtIndex:(NSInteger)section {
  97. UIEdgeInsets sectionInset;
  98. if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
  99. sectionInset = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:section];
  100. } else {
  101. sectionInset = self.sectionInset;
  102. }
  103. CGFloat width = self.collectionView.bounds.size.width - sectionInset.left - sectionInset.right;
  104. NSInteger columnCount = [self columnCountForSection:section];
  105. CGFloat columnSpacing = self.minimumColumnSpacing;
  106. if ([self.delegate respondsToSelector:@selector(collectionView:layout:minimumColumnSpacingForSectionAtIndex:)]) {
  107. columnSpacing = [self.delegate collectionView:self.collectionView layout:self minimumColumnSpacingForSectionAtIndex:section];
  108. }
  109. return CHTFloorCGFloat((width - (columnCount - 1) * columnSpacing) / columnCount);
  110. }
  111. #pragma mark - Private Accessors
  112. - (NSMutableDictionary *)headersAttribute {
  113. if (!_headersAttribute) {
  114. _headersAttribute = [NSMutableDictionary dictionary];
  115. }
  116. return _headersAttribute;
  117. }
  118. - (NSMutableDictionary *)footersAttribute {
  119. if (!_footersAttribute) {
  120. _footersAttribute = [NSMutableDictionary dictionary];
  121. }
  122. return _footersAttribute;
  123. }
  124. - (NSMutableArray *)unionRects {
  125. if (!_unionRects) {
  126. _unionRects = [NSMutableArray array];
  127. }
  128. return _unionRects;
  129. }
  130. - (NSMutableArray *)columnHeights {
  131. if (!_columnHeights) {
  132. _columnHeights = [NSMutableArray array];
  133. }
  134. return _columnHeights;
  135. }
  136. - (NSMutableArray *)allItemAttributes {
  137. if (!_allItemAttributes) {
  138. _allItemAttributes = [NSMutableArray array];
  139. }
  140. return _allItemAttributes;
  141. }
  142. - (NSMutableArray *)sectionItemAttributes {
  143. if (!_sectionItemAttributes) {
  144. _sectionItemAttributes = [NSMutableArray array];
  145. }
  146. return _sectionItemAttributes;
  147. }
  148. - (id <CHTCollectionViewDelegateWaterfallLayout> )delegate {
  149. return (id <CHTCollectionViewDelegateWaterfallLayout> )self.collectionView.delegate;
  150. }
  151. #pragma mark - Init
  152. - (void)commonInit {
  153. _columnCount = 2;
  154. _minimumColumnSpacing = 10;
  155. _minimumInteritemSpacing = 10;
  156. _headerHeight = 0;
  157. _footerHeight = 0;
  158. _sectionInset = UIEdgeInsetsZero;
  159. _headerInset = UIEdgeInsetsZero;
  160. _footerInset = UIEdgeInsetsZero;
  161. _itemRenderDirection = CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst;
  162. }
  163. - (id)init {
  164. if (self = [super init]) {
  165. [self commonInit];
  166. }
  167. return self;
  168. }
  169. - (id)initWithCoder:(NSCoder *)aDecoder {
  170. if (self = [super initWithCoder:aDecoder]) {
  171. [self commonInit];
  172. }
  173. return self;
  174. }
  175. #pragma mark - Methods to Override
  176. - (void)prepareLayout {
  177. [super prepareLayout];
  178. [self.headersAttribute removeAllObjects];
  179. [self.footersAttribute removeAllObjects];
  180. [self.unionRects removeAllObjects];
  181. [self.columnHeights removeAllObjects];
  182. [self.allItemAttributes removeAllObjects];
  183. [self.sectionItemAttributes removeAllObjects];
  184. NSInteger numberOfSections = [self.collectionView numberOfSections];
  185. if (numberOfSections == 0) {
  186. return;
  187. }
  188. NSAssert([self.delegate conformsToProtocol:@protocol(CHTCollectionViewDelegateWaterfallLayout)], @"UICollectionView's delegate should conform to CHTCollectionViewDelegateWaterfallLayout protocol");
  189. NSAssert(self.columnCount > 0 || [self.delegate respondsToSelector:@selector(collectionView:layout:columnCountForSection:)], @"UICollectionViewWaterfallLayout's columnCount should be greater than 0, or delegate must implement columnCountForSection:");
  190. // Initialize variables
  191. NSInteger idx = 0;
  192. for (NSInteger section = 0; section < numberOfSections; section++) {
  193. NSInteger columnCount = [self columnCountForSection:section];
  194. NSMutableArray *sectionColumnHeights = [NSMutableArray arrayWithCapacity:columnCount];
  195. for (idx = 0; idx < columnCount; idx++) {
  196. [sectionColumnHeights addObject:@(0)];
  197. }
  198. [self.columnHeights addObject:sectionColumnHeights];
  199. }
  200. // Create attributes
  201. CGFloat top = 0;
  202. UICollectionViewLayoutAttributes *attributes;
  203. for (NSInteger section = 0; section < numberOfSections; ++section) {
  204. /*
  205. * 1. Get section-specific metrics (minimumInteritemSpacing, sectionInset)
  206. */
  207. CGFloat minimumInteritemSpacing;
  208. if ([self.delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]) {
  209. minimumInteritemSpacing = [self.delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:section];
  210. } else {
  211. minimumInteritemSpacing = self.minimumInteritemSpacing;
  212. }
  213. CGFloat columnSpacing = self.minimumColumnSpacing;
  214. if ([self.delegate respondsToSelector:@selector(collectionView:layout:minimumColumnSpacingForSectionAtIndex:)]) {
  215. columnSpacing = [self.delegate collectionView:self.collectionView layout:self minimumColumnSpacingForSectionAtIndex:section];
  216. }
  217. UIEdgeInsets sectionInset;
  218. if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
  219. sectionInset = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:section];
  220. } else {
  221. sectionInset = self.sectionInset;
  222. }
  223. CGFloat width = self.collectionView.bounds.size.width - sectionInset.left - sectionInset.right;
  224. NSInteger columnCount = [self columnCountForSection:section];
  225. CGFloat itemWidth = CHTFloorCGFloat((width - (columnCount - 1) * columnSpacing) / columnCount);
  226. /*
  227. * 2. Section header
  228. */
  229. CGFloat headerHeight;
  230. if ([self.delegate respondsToSelector:@selector(collectionView:layout:heightForHeaderInSection:)]) {
  231. headerHeight = [self.delegate collectionView:self.collectionView layout:self heightForHeaderInSection:section];
  232. } else {
  233. headerHeight = self.headerHeight;
  234. }
  235. UIEdgeInsets headerInset;
  236. if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForHeaderInSection:)]) {
  237. headerInset = [self.delegate collectionView:self.collectionView layout:self insetForHeaderInSection:section];
  238. } else {
  239. headerInset = self.headerInset;
  240. }
  241. top += headerInset.top;
  242. if (headerHeight > 0) {
  243. attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CHTCollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
  244. attributes.frame = CGRectMake(headerInset.left,
  245. top,
  246. self.collectionView.bounds.size.width - (headerInset.left + headerInset.right),
  247. headerHeight);
  248. self.headersAttribute[@(section)] = attributes;
  249. [self.allItemAttributes addObject:attributes];
  250. top = CGRectGetMaxY(attributes.frame) + headerInset.bottom;
  251. }
  252. top += sectionInset.top;
  253. for (idx = 0; idx < columnCount; idx++) {
  254. self.columnHeights[section][idx] = @(top);
  255. }
  256. /*
  257. * 3. Section items
  258. */
  259. NSInteger itemCount = [self.collectionView numberOfItemsInSection:section];
  260. NSMutableArray *itemAttributes = [NSMutableArray arrayWithCapacity:itemCount];
  261. // Item will be put into shortest column.
  262. for (idx = 0; idx < itemCount; idx++) {
  263. NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section];
  264. NSUInteger columnIndex = [self nextColumnIndexForItem:idx inSection:section];
  265. CGFloat xOffset = sectionInset.left + (itemWidth + columnSpacing) * columnIndex;
  266. CGFloat yOffset = [self.columnHeights[section][columnIndex] floatValue];
  267. CGSize itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
  268. CGFloat itemHeight = 0;
  269. if (itemSize.height > 0 && itemSize.width > 0) {
  270. itemHeight = CHTFloorCGFloat(itemSize.height * itemWidth / itemSize.width);
  271. }
  272. attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
  273. attributes.frame = CGRectMake(xOffset, yOffset, itemWidth, itemHeight);
  274. [itemAttributes addObject:attributes];
  275. [self.allItemAttributes addObject:attributes];
  276. self.columnHeights[section][columnIndex] = @(CGRectGetMaxY(attributes.frame) + minimumInteritemSpacing);
  277. }
  278. [self.sectionItemAttributes addObject:itemAttributes];
  279. /*
  280. * 4. Section footer
  281. */
  282. CGFloat footerHeight;
  283. NSUInteger columnIndex = [self longestColumnIndexInSection:section];
  284. if (((NSArray *)self.columnHeights[section]).count > 0) {
  285. top = [self.columnHeights[section][columnIndex] floatValue] - minimumInteritemSpacing + sectionInset.bottom;
  286. } else {
  287. top = 0;
  288. }
  289. if ([self.delegate respondsToSelector:@selector(collectionView:layout:heightForFooterInSection:)]) {
  290. footerHeight = [self.delegate collectionView:self.collectionView layout:self heightForFooterInSection:section];
  291. } else {
  292. footerHeight = self.footerHeight;
  293. }
  294. UIEdgeInsets footerInset;
  295. if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForFooterInSection:)]) {
  296. footerInset = [self.delegate collectionView:self.collectionView layout:self insetForFooterInSection:section];
  297. } else {
  298. footerInset = self.footerInset;
  299. }
  300. top += footerInset.top;
  301. if (footerHeight > 0) {
  302. attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CHTCollectionElementKindSectionFooter withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
  303. attributes.frame = CGRectMake(footerInset.left,
  304. top,
  305. self.collectionView.bounds.size.width - (footerInset.left + footerInset.right),
  306. footerHeight);
  307. self.footersAttribute[@(section)] = attributes;
  308. [self.allItemAttributes addObject:attributes];
  309. top = CGRectGetMaxY(attributes.frame) + footerInset.bottom;
  310. }
  311. for (idx = 0; idx < columnCount; idx++) {
  312. self.columnHeights[section][idx] = @(top);
  313. }
  314. } // end of for (NSInteger section = 0; section < numberOfSections; ++section)
  315. // Build union rects
  316. idx = 0;
  317. NSInteger itemCounts = [self.allItemAttributes count];
  318. while (idx < itemCounts) {
  319. CGRect unionRect = ((UICollectionViewLayoutAttributes *)self.allItemAttributes[idx]).frame;
  320. NSInteger rectEndIndex = MIN(idx + unionSize, itemCounts);
  321. for (NSInteger i = idx + 1; i < rectEndIndex; i++) {
  322. unionRect = CGRectUnion(unionRect, ((UICollectionViewLayoutAttributes *)self.allItemAttributes[i]).frame);
  323. }
  324. idx = rectEndIndex;
  325. [self.unionRects addObject:[NSValue valueWithCGRect:unionRect]];
  326. }
  327. }
  328. - (CGSize)collectionViewContentSize {
  329. NSInteger numberOfSections = [self.collectionView numberOfSections];
  330. if (numberOfSections == 0) {
  331. return CGSizeZero;
  332. }
  333. CGSize contentSize = self.collectionView.bounds.size;
  334. contentSize.height = [[[self.columnHeights lastObject] firstObject] floatValue];
  335. if (contentSize.height < self.minimumContentHeight) {
  336. contentSize.height = self.minimumContentHeight;
  337. }
  338. return contentSize;
  339. }
  340. - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
  341. if (path.section >= [self.sectionItemAttributes count]) {
  342. return nil;
  343. }
  344. if (path.item >= [self.sectionItemAttributes[path.section] count]) {
  345. return nil;
  346. }
  347. return (self.sectionItemAttributes[path.section])[path.item];
  348. }
  349. - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
  350. UICollectionViewLayoutAttributes *attribute = nil;
  351. if ([kind isEqualToString:CHTCollectionElementKindSectionHeader]) {
  352. attribute = self.headersAttribute[@(indexPath.section)];
  353. } else if ([kind isEqualToString:CHTCollectionElementKindSectionFooter]) {
  354. attribute = self.footersAttribute[@(indexPath.section)];
  355. }
  356. return attribute;
  357. }
  358. - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
  359. NSInteger i;
  360. NSInteger begin = 0, end = self.unionRects.count;
  361. NSMutableDictionary *cellAttrDict = [NSMutableDictionary dictionary];
  362. NSMutableDictionary *supplHeaderAttrDict = [NSMutableDictionary dictionary];
  363. NSMutableDictionary *supplFooterAttrDict = [NSMutableDictionary dictionary];
  364. NSMutableDictionary *decorAttrDict = [NSMutableDictionary dictionary];
  365. for (i = 0; i < self.unionRects.count; i++) {
  366. if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue])) {
  367. begin = i * unionSize;
  368. break;
  369. }
  370. }
  371. for (i = self.unionRects.count - 1; i >= 0; i--) {
  372. if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue])) {
  373. end = MIN((i + 1) * unionSize, self.allItemAttributes.count);
  374. break;
  375. }
  376. }
  377. for (i = begin; i < end; i++) {
  378. UICollectionViewLayoutAttributes *attr = self.allItemAttributes[i];
  379. if (CGRectIntersectsRect(rect, attr.frame)) {
  380. switch (attr.representedElementCategory) {
  381. case UICollectionElementCategorySupplementaryView:
  382. if ([attr.representedElementKind isEqualToString:CHTCollectionElementKindSectionHeader]) {
  383. supplHeaderAttrDict[attr.indexPath] = attr;
  384. } else if ([attr.representedElementKind isEqualToString:CHTCollectionElementKindSectionFooter]) {
  385. supplFooterAttrDict[attr.indexPath] = attr;
  386. }
  387. break;
  388. case UICollectionElementCategoryDecorationView:
  389. decorAttrDict[attr.indexPath] = attr;
  390. break;
  391. case UICollectionElementCategoryCell:
  392. cellAttrDict[attr.indexPath] = attr;
  393. break;
  394. }
  395. }
  396. }
  397. NSArray *result = [cellAttrDict.allValues arrayByAddingObjectsFromArray:supplHeaderAttrDict.allValues];
  398. result = [result arrayByAddingObjectsFromArray:supplFooterAttrDict.allValues];
  399. result = [result arrayByAddingObjectsFromArray:decorAttrDict.allValues];
  400. return result;
  401. }
  402. - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
  403. CGRect oldBounds = self.collectionView.bounds;
  404. if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) {
  405. return YES;
  406. }
  407. return NO;
  408. }
  409. #pragma mark - Private Methods
  410. /**
  411. * Find the shortest column.
  412. *
  413. * @return index for the shortest column
  414. */
  415. - (NSUInteger)shortestColumnIndexInSection:(NSInteger)section {
  416. __block NSUInteger index = 0;
  417. __block CGFloat shortestHeight = MAXFLOAT;
  418. [self.columnHeights[section] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  419. CGFloat height = [obj floatValue];
  420. if (height < shortestHeight) {
  421. shortestHeight = height;
  422. index = idx;
  423. }
  424. }];
  425. return index;
  426. }
  427. /**
  428. * Find the longest column.
  429. *
  430. * @return index for the longest column
  431. */
  432. - (NSUInteger)longestColumnIndexInSection:(NSInteger)section {
  433. __block NSUInteger index = 0;
  434. __block CGFloat longestHeight = 0;
  435. [self.columnHeights[section] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  436. CGFloat height = [obj floatValue];
  437. if (height > longestHeight) {
  438. longestHeight = height;
  439. index = idx;
  440. }
  441. }];
  442. return index;
  443. }
  444. /**
  445. * Find the index for the next column.
  446. *
  447. * @return index for the next column
  448. */
  449. - (NSUInteger)nextColumnIndexForItem:(NSInteger)item inSection:(NSInteger)section {
  450. NSUInteger index = 0;
  451. NSInteger columnCount = [self columnCountForSection:section];
  452. switch (self.itemRenderDirection) {
  453. case CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst:
  454. index = [self shortestColumnIndexInSection:section];
  455. break;
  456. case CHTCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight:
  457. index = (item % columnCount);
  458. break;
  459. case CHTCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft:
  460. index = (columnCount - 1) - (item % columnCount);
  461. break;
  462. default:
  463. index = [self shortestColumnIndexInSection:section];
  464. break;
  465. }
  466. return index;
  467. }
  468. @end