2015-08-03 120 views
7

我試圖複製一個場景,其中有一個集合視圖一直到頂部,集合視圖的第一行頂部的標題視圖,然後是標題部分視圖(對於indexPath.section == 1),它保持標題視圖不重疊或在其下面。UICollectionView部分標題粘到某個Y

我已經實現了自定義佈局,使標題變得粘稠。但是我不能堅持標題視圖(所以在'origin.y + height'之後),它會堅持到集合視圖的頂部。

這裏是我的IB結構:

enter image description here

與自定義佈局聯繫起來:

enter image description here

這裏是理想的方案:

滾動前:

enter image description here

滾動後:

enter image description here

這裏是我的代碼:

@implementation LLCollectionCustomFlowLayout 

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 
{ 
    return YES; 
} 

- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect { 

    NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; 

    NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet]; 
    for (NSUInteger idx=0; idx<[answer count]; idx++) { 
     UICollectionViewLayoutAttributes *layoutAttributes = answer[idx]; 

     if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) { 
      [missingSections addIndex:layoutAttributes.indexPath.section]; // remember that we need to layout header for this section 
     } 
     if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 
      [answer removeObjectAtIndex:idx]; // remove layout of header done by our super, we will do it right later 
      idx--; 
     } 
    } 

    // layout all headers needed for the rect using self code 
    [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { 
     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; 
     UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; 
     if (layoutAttributes != nil) { 
      [answer addObject:layoutAttributes]; 
     } 
    }]; 

    return answer; 
} 


- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { 
    UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; 
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { 
     UICollectionView * const cv = self.collectionView; 
     CGPoint const contentOffset = cv.contentOffset; 
     CGPoint nextHeaderOrigin = CGPointMake(INFINITY, INFINITY); 

     if (indexPath.section+1 < [cv numberOfSections]) { 
      UICollectionViewLayoutAttributes *nextHeaderAttributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexPath.section+1]]; 
      nextHeaderOrigin = nextHeaderAttributes.frame.origin; 
     } 

     CGRect frame = attributes.frame; 
     if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { 

      frame.origin.y = MIN(MAX(contentOffset.y, frame.origin.y), nextHeaderOrigin.y - CGRectGetHeight(frame)); 


     } 
     else { // UICollectionViewScrollDirectionHorizontal 
      frame.origin.x = MIN(MAX(contentOffset.x, frame.origin.x), nextHeaderOrigin.x - CGRectGetWidth(frame)); 
     } 
     attributes.zIndex = 1024; 
     attributes.frame = frame; 
    } 
    return attributes; 
} 

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { 
    UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; 
    return attributes; 
} 
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { 
    UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; 
    return attributes; 
} 

@end 

回答

3

我已經解決了這一個粗略的辦法:

- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect { 

    NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; 

    NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet]; 
    for (NSUInteger idx=0; idx<[answer count]; idx++) { 
     UICollectionViewLayoutAttributes *layoutAttributes = answer[idx]; 

     if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) { 
      [missingSections addIndex:layoutAttributes.indexPath.section]; // remember that we need to layout header for this section 
     } 
     if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 
      [answer removeObjectAtIndex:idx]; // remove layout of header done by our super, we will do it right later 
      idx--; 
     } 
    } 

    // layout all headers needed for the rect using self code 
    [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { 
     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; 
     UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; 
     if (layoutAttributes != nil) { 
      [answer addObject:layoutAttributes]; 
     } 

     if (idx == 1) { 
      NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; 
      UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; 
      if (layoutAttributes != nil) { 
       [answer addObject:layoutAttributes]; 
      } 
     } 
    }]; 

    return answer; 
} 

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { 
    UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; 
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { 
     UICollectionView * const cv = self.collectionView; 
     CGPoint const contentOffset = cv.contentOffset; 
     CGPoint previousHeaderOrigin = CGPointMake(INFINITY, INFINITY); 
     CGFloat previousSectionY = 0; 

     if (indexPath.section == 1) { 
      UICollectionViewLayoutAttributes *previousHeaderAttributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; 
      previousSectionY = previousHeaderAttributes.frame.origin.y; 
     } 

     CGRect frame = attributes.frame; 
     if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { 

      if (indexPath.section == 0) { 
       frame.origin.y = MAX(contentOffset.y, frame.origin.y); 
      }else{ 
      frame.origin.y = MAX(previousSectionY+frame.size.height, frame.origin.y); 
      } 
     } 
     else { // UICollectionViewScrollDirectionHorizontal 
      frame.origin.x = MIN(MAX(contentOffset.x, frame.origin.x), previousHeaderOrigin.x - CGRectGetWidth(frame)); 
     } 
     attributes.zIndex = 1024; 
     attributes.frame = frame; 
    } 
    return attributes; 
} 

此外,在該文件中,我有的CollectionView:

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{ 

    if (section == 0) { 
     return UIEdgeInsetsMake(-kHEIGHT_TOP_VIEW, 0, 0, 0); 
    }else 
     return UIEdgeInsetsMake(0, 0, 0, 0); 

} 

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section 
{ 

     return CGSizeMake(self.collectionView.frame.size.width, -kHEIGHT_TOP_VIEW); 
} 

如果你有更好的答案,讓我知道,所以我可以接受你的獎勵給你賞金。

4

問題就在這裏:

frame.origin.y = MIN(MAX(contentOffset.y, frame.origin.y), nextHeaderOrigin.y - CGRectGetHeight(frame)); 

當標題是最後一個,nextHeaderOrigin = CGPointMake(INFINITY, INFINITY)。這就是爲什麼frame.origin.y將是MAX(contentOffset.y, frame.origin.y),並堅持收集視圖的頂部。你應該在接下來的方式更新layoutAttributesForSupplementaryViewOfKind

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { 
    UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; 
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { 
     // for the header in the section #0, we have "sticky" behaviour by default 
     if (indexPath.section == 0) { 
      return attributes; 
     } 
     CGPoint contentOffset = self.collectionView.contentOffset; 
     UICollectionViewLayoutAttributes *prevHeaderAttributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexPath.section - 1]]; 
     CGFloat prevHeaderMaxY = CGRectGetMaxY(prevHeaderAttributes.frame); // The maximum Y value from the previous header should be the position we use to "stick" the current one 
     CGFloat prevHeaderMaxX = CGRectGetMaxX(prevHeaderAttributes.frame); 
     CGRect frame = attributes.frame; 
     if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { 
      frame.origin.y = MAX(frame.origin.y, prevHeaderMaxY); 
     } 
     else { // UICollectionViewScrollDirectionHorizontal 
      frame.origin.x = MAX(frame.origin.x, prevHeaderMaxX); 
     } 
     attributes.frame = frame; 
    } 
    return attributes; 
} 
+0

感謝您的回答。它雖然不起作用。修復兩個編譯器警告(prevHeaderMaxY和prevHeaderMaxX應該是CGFloats)之後,標題不再粘在頂端。我相信這是由於沒有第[0]個標題。由於它是零,它無法找到棘手的問題。 – jonypz

+0

我已經把第一個標題放回去了,但它仍然不起作用。現在標題不再粘着了。 – jonypz

+0

嘗試使用'UICollectionViewLayoutAttributes * prevHeaderAttributes = [self layoutAttributesFor ...'而不是'UICollectionViewLayoutAttributes * prevHeaderAttributes = [super ...'。超級調用不會返回prev的正確屬性。標題 – sgl0v