2013-03-05 15 views
20

I found a blog on how to make sticky headersこれは素晴らしい動作です。唯一のことは、sectionInsertsを考慮に入れるとは思わないということです。スティッキーヘッダーを持つUICollectionView

これはそのが見えるように意図した方法である:

enter image description here

私は私の挿入を持っている:

collectionViewFlowLayout.sectionInset = UIEdgeInsetsMake(16, 16, 16, 16); 

粘着性のヘッダと、それは16 pixles下に移動されますenter image description here

私は錫元のコードで王と私は問題は最後の部分であると思う:

layoutAttributes.frame = (CGRect){ 
    .origin = CGPointMake(origin.x, origin.y), 
    .size = layoutAttributes.frame.size 

私はorigin.y - 16に変更した場合、ヘッダは右の場所で起動しますが、押し上げられたときに、ヘッドの16個のピクセルはオフに行きます画面:

enter image description here

私はそれは、アカウントsectionInsectsに取るために取得するかどうかはわかりません。誰でも助けることができますか?ここで

ブログからフルのコードです:スクロール水平および垂直を処理すると、アカウントにsectionInsetsを取るためにトッド・レイニーによって

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

    NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; 
    UICollectionView * const cv = self.collectionView; 
    CGPoint const contentOffset = cv.contentOffset; 

    NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet]; 
    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 
     if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) { 
      [missingSections addIndex:layoutAttributes.indexPath.section]; 
     } 
    } 
    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 
     if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 
      [missingSections removeIndex:layoutAttributes.indexPath.section]; 
     } 
    } 

    [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { 

     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; 

     UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; 

     [answer addObject:layoutAttributes]; 
    }]; 

    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 

     if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 

      NSInteger section = layoutAttributes.indexPath.section; 
      NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section]; 

      NSIndexPath *firstCellIndexPath = [NSIndexPath indexPathForItem:0 inSection:section]; 
      NSIndexPath *lastCellIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section]; 

      UICollectionViewLayoutAttributes *firstCellAttrs = [self layoutAttributesForItemAtIndexPath:firstCellIndexPath]; 
      UICollectionViewLayoutAttributes *lastCellAttrs = [self layoutAttributesForItemAtIndexPath:lastCellIndexPath]; 

      CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame); 
      CGPoint origin = layoutAttributes.frame.origin; 
      origin.y = MIN(
       MAX(
        contentOffset.y, 
        (CGRectGetMinY(firstCellAttrs.frame) - headerHeight) 
       ), 
       (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight) 
      ); 

      layoutAttributes.zIndex = 1024; 
      layoutAttributes.frame = (CGRect){ 
       .origin = origin, 
       .size = layoutAttributes.frame.size 
      }; 
     } 
    } 

    return answer; 
} 
+0

は今までこれを理解しますか?私は現在同じ問題があります。 – elsurudo

+0

Nope。透明なヘッダーでそれを偽って見てください。 – Padin215

+0

ブログのリンクは非常に便利です。参照を共有してくれてありがとう –

答えて

31

は修正:

https://gist.github.com/evadne/4544569

@implementation StickyHeaderFlowLayout 

- (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; 
} 

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

@end 
+2

'frame'の計算のために' contentInset'を尊重すべきです。 'frame.origin.y = MIN(MAX(contentOffset.y + cv.contentInset.top、frame.origin.y)、nextHeaderOrigin.y - CGRectGetHeight(frame)); ' iOS7では0でない可能性があり、ヘッダーがナビゲーションバーの下に収まる可能性があります。 –

+0

セクションに0個の項目がある場合、ヘッダーは表示されず、スペースも表示されます。あなたはそれのためのスペース、 を表示しないことを選ぶかもしれませんが、セクションが0の項目を持っている場合でも、単にヘッダーを表示することができます。この場合、最初の '' answer'''で遭遇したすべてのヘッダーの '' missingSections'''にセクションを追加する必要があります。 – MrJre

+0

@MrJe - セクションに行がない場合をどのように修正しますか?私は残念ながらあなたのコメントを組み込むことができません...感謝! – trdavidson

2

これは本当に良い解決策であり、完璧に機能します。ただし、shouldInvalidateLayoutForBoundsChangeからYESを返す必要があるため、ビューがスクロールするたびにprepareLayoutが呼び出されます。さて、IFあなたのprepareLayoutはレイアウト属性を作成する責任がありますが、それはかなり一般的です。これはスクロールのパフォーマンスに非常に影響します。

解決策の1つは、prepareLayoutでレイアウト属性を作成せず、代わりにinvalidateLayoutを呼び出す前に明示的に呼び出す個別のメソッドで行うことです。 UICollectionViewはprepareLayoutを呼び出し、レイアウトについて知っておく必要があると感じると、この解決法もそのようなケースを処理します。

2

このコードは

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 
     NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; 
     UICollectionView * const cv = self.collectionView; 
     //CLS_LOG(@"Number of sections = %d", [cv numberOfSections]); 
     CGPoint const contentOffset = cv.contentOffset; 

    //CLS_LOG(@"Adding missing sections"); 
    NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet]; 
    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 
     if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) { 
      [missingSections addIndex:layoutAttributes.indexPath.section]; 
     } 
    } 
    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 
     if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 
      [missingSections removeIndex:layoutAttributes.indexPath.section]; 
     } 
    } 

    [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { 

     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; 

     UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; 

     [answer addObject:layoutAttributes]; 

    }]; 

    NSInteger numberOfSections = [cv numberOfSections]; 

    //CLS_LOG(@"For loop"); 
    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 

     if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 

      NSInteger section = layoutAttributes.indexPath.section; 
      //CLS_LOG(@"Customizing layout attribute for header in section %d with number of items = %d", section, [cv numberOfItemsInSection:section]); 

      if (section < numberOfSections) { 
       NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section]; 

       NSIndexPath *firstObjectIndexPath = [NSIndexPath indexPathForItem:0 inSection:section]; 
       NSIndexPath *lastObjectIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section]; 

       BOOL cellsExist; 
       UICollectionViewLayoutAttributes *firstObjectAttrs; 
       UICollectionViewLayoutAttributes *lastObjectAttrs; 

       if (numberOfItemsInSection > 0) { // use cell data if items exist 
        cellsExist = YES; 
        firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath]; 
        lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath]; 
       } else { // else use the header and footer 
        cellsExist = NO; 
        firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader 
                      atIndexPath:firstObjectIndexPath]; 
        lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter 
                      atIndexPath:lastObjectIndexPath]; 

       } 

       CGFloat topHeaderHeight = (cellsExist) ? CGRectGetHeight(layoutAttributes.frame) : 0; 
       CGFloat bottomHeaderHeight = CGRectGetHeight(layoutAttributes.frame); 
       CGRect frameWithEdgeInsets = UIEdgeInsetsInsetRect(layoutAttributes.frame, 
                    cv.contentInset); 

       CGPoint origin = frameWithEdgeInsets.origin; 

       origin.y = MIN(
           MAX(
            contentOffset.y + cv.contentInset.top, 
            (CGRectGetMinY(firstObjectAttrs.frame) - topHeaderHeight) 
            ), 
           (CGRectGetMaxY(lastObjectAttrs.frame) - bottomHeaderHeight) 
           ); 

       layoutAttributes.zIndex = 1024; 
       layoutAttributes.frame = (CGRect){ 
        .origin = origin, 
        .size = layoutAttributes.frame.size 
       }; 
      } 
     } 

    } 

    return answer; 

} 

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

はこの男を試してみてください私の作品...上記の

0

どれも私のために働いていません。私は、私のインセットの世話をして、私にPhoto.appスタイルのスクロールコレクションを提供するクリーンなレイアウトを探していました。

私は、エッジインセットの世話をするために、のソリューションを採用しました。分かりやすくするため、私はここに完全な解決策を添付します。しかし、以下の要点から完全な解決策を得ることができます:#3e1955a4492a897e677f

@implementation SpringboardLayout 

- (id)init 
{ 
    if (self = [super init]) 
    { 
     self.headerReferenceSize = CGSizeMake(0, 50); 
     self.footerReferenceSize = CGSizeMake(0, 0); 
     self.sectionInset = UIEdgeInsetsMake(10, 10, 80, 10); 
     self.scrollDirection = UICollectionViewScrollDirectionVertical; 
     self.minimumInteritemSpacing = 10; 
     self.minimumLineSpacing = 10; 
     if(IS_IPHONE_6 || IS_IPHONE_6PLUS) { 
      self.itemSize = CGSizeMake(100, 128); 
     } else { 
      self.itemSize = CGSizeMake(80, 108); 
     } 
    } 
    return self; 
} 

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

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

    NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; 
    UICollectionView * const cv = self.collectionView; 
    CGPoint const contentOffset = cv.contentOffset; 

    NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet]; 

    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 
     if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) { 
      [missingSections addIndex:layoutAttributes.indexPath.section]; 
     } else if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 
      [missingSections removeIndex:layoutAttributes.indexPath.section]; 
     } 
    } 

    [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { 

     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; 

     UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; 

     [answer addObject:layoutAttributes]; 

    }]; 

    for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { 

     if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { 

      NSInteger section = layoutAttributes.indexPath.section; 
      NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section]; 

      NSIndexPath *firstObjectIndexPath = [NSIndexPath indexPathForItem:0 inSection:section]; 
      NSIndexPath *lastObjectIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section]; 

      BOOL cellsExist; 
      UICollectionViewLayoutAttributes *firstObjectAttrs; 
      UICollectionViewLayoutAttributes *lastObjectAttrs; 

      if (numberOfItemsInSection > 0) { // use cell data if items exist 
       cellsExist = YES; 
       firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath]; 
       lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath]; 
      } else { // else use the header and footer 
       cellsExist = NO; 
       firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader 
                     atIndexPath:firstObjectIndexPath]; 
       lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter 
                     atIndexPath:lastObjectIndexPath]; 

      } 

      CGFloat topHeaderHeight = (cellsExist) ? CGRectGetHeight(layoutAttributes.frame) : 0; 
      CGFloat bottomHeaderHeight = CGRectGetHeight(layoutAttributes.frame); 
      CGRect frameWithEdgeInsets = UIEdgeInsetsInsetRect(layoutAttributes.frame, 
                   cv.contentInset); 

      CGPoint origin = frameWithEdgeInsets.origin; 


      origin.y = MIN(MAX(contentOffset.y + cv.contentInset.top, 
           (CGRectGetMinY(firstObjectAttrs.frame) - topHeaderHeight - self.sectionInset.top)) 
          ,(CGRectGetMaxY(lastObjectAttrs.frame) - bottomHeaderHeight + self.sectionInset.bottom)); 
      layoutAttributes.zIndex = 1024; 
      layoutAttributes.frame = (CGRect){ 
       .origin = origin, 
       .size = layoutAttributes.frame.size 
      }; 
     } 
    } 
    return answer; 
} 

@end 
+0

最初のセクションに行がない場合、これは機能しません。セクションヘッダーの下にセクションヘッダーのスペースが作成されます。 – trdavidson

57

UICollectionViewFlowLayoutのサブクラスを記述する必要がないため、iOS 9+のための最も単純なソリューションです。次のコードcollectionViewの使用とのViewControllerののviewDidLoadで

let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout // casting is required because UICollectionViewLayout doesn't offer header pin. Its feature of UICollectionViewFlowLayout 
layout?.sectionHeadersPinToVisibleBounds = true 
+0

フローレイアウトを使用している場合、絶対に完璧なソリューションです:) – luk2302

関連する問題