web-dev-qa-db-ja.com

プログラムでUICollectionView内の補足ビューにスクロールします

UICollectionViewを使用して写真をセクションに表示しています。各セクションにはヘッダーとして補足ビューがあり、メソッドviewForSupplementaryElementOfKindを介して提供されます。

ユーザーがセクション間をジャンプできるようにするスクラバーが側面にあります。今のところ、scrollToItemAtIndexPath:atScrollPosition:animated:を使用してセクションの最初の項目にスクロールしていますが、本当に必要なのは、そのセクションのヘッダーが最初のセルではなく画面の上部に表示されるようにcollectionViewをスクロールすることです。これを行うための明白な方法がわかりません。回避策はありますか?

セクションの最初のアイテムまでスクロールし、それを補足の高さに加えて、アイテムとヘッダーの間のオフセットでオフセットすることができると思います(contentViewのポイント座標にスクロールする方法があります)。しかし、もっと簡単な方法があれば知りたいのですが。

ありがとう。

21
VaporwareWolf

あなたできませんこれにはscrollToItemAtIndexPath:atScrollPosition:animatedを使用します。

うまくいけば、将来的にはscrollToSupplementaryElementOfKind:atIndexPath:のような新しいメソッドが追加されるでしょうが、今のところ、唯一の方法はcontentOffsetを直接操作することです

以下のコードは、FlowLayoutを使用してヘッダーを垂直方向に上にスクロールする方法を示しています。水平スクロールでも同じことができますが、他のレイアウトタイプでもこのアイデアを使用できます。

NSIndexPath *indexPath = ... // indexPath of your header, item must be 0

CGFloat offsetY = [collectionView layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath].frame.Origin.y;

CGFloat contentInsetY = self.contentInset.top;
CGFloat sectionInsetY = ((UICollectionViewFlowLayout *)collectionView.collectionViewLayout).sectionInset.top;

[collectionView setContentOffset:CGPointMake(collectionView.contentOffset.x, offsetY - contentInsetY - sectionInsetY) animated:YES];

ゼロ以外のcontentInsetがある場合(iOS 7のように、スクロールビューがバーの下に展開される場合)、図に示すように、offsetYからそれを差し引く必要があることに注意してください。 sectionInsetについても同じです。

更新:

このコードは、レイアウトを使用してオフセットを計算するため、レイアウトが準備された「有効な」状態にあることを前提としています。レイアウトは、コレクションビューがそのコンテンツを表示するときに準備されます。

上記のコードの前に[_collectionView.collectionViewLayout prepareLayout]を呼び出すと、まだ表示されていないコレクションビューをスクロールする必要がある場合に役立ちます(viewDidLoadなどから)。 layoutIfNeededの呼び出し(@Vrasidasがコメントで示唆しているように)も、レイアウトを準備するため、機能するはずです。

38

Swiftのソリューション、

let section: = 0 // Top
if let cv = self.collectionView {

    cv.layoutIfNeeded()

    let indexPath = NSIndexPath(forItem: 1, inSection: section)
    if let attributes =  cv.layoutAttributesForSupplementaryElementOfKind(UICollectionElementKindSectionHeader, atIndexPath: indexPath) {

        let topOfHeader = CGPointMake(0, attributes.frame.Origin.y - cv.contentInset.top)
        cv.setContentOffset(topOfHeader, animated:true)
    }
}

Gene De Lisaの小道具: http://www.rockhoppertech.com/blog/scroll-to-uicollectionview-header/

6
clozach
// scroll to selected index
NSIndexPath* cellIndexPath = [NSIndexPath indexPathForItem:0 inSection:sectionIndex];
UICollectionViewLayoutAttributes* attr = [self.collectionView.collectionViewLayout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:cellIndexPath];
UIEdgeInsets insets = self.collectionView.scrollIndicatorInsets;

CGRect rect = attr.frame;
rect.size = self.collectionView.frame.size;
rect.size.height -= insets.top + insets.bottom;
CGFloat offset = (rect.Origin.y + rect.size.height) - self.collectionView.contentSize.height;
if ( offset > 0.0 ) rect = CGRectOffset(rect, 0, -offset);

[self.collectionView scrollRectToVisible:rect animated:YES];
2
Cherpak Evgeny

ソリューションが管理しないことの1つは、セクションヘッダーを固定している場合です。メソッドは固定されていないヘッダーで正常に機能しますが、ヘッダーが固定されている場合、現在のセクションの上のセクションにスクロールしているときに、セクションヘッダーが表示されると停止します(セクションの一番下の行になります)。それが望ましい場合もありますが、目標はセクションの上部を画面の上部に配置することだと思います。

その場合、上記の方法を採用して少し調整する必要があります。例えば:

UICollectionView *cv = self.collectionView;
CGFloat contentInsetY = cv.contentInset.top;
CGFloat offsetY = [cv layoutAttributesForItemAtIndexPath:ip].frame.Origin.y;
CGFloat sectionHeight =
[cv layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:ip].frame.size.height;
[cv setContentOffset:CGPointMake(cv.contentOffset.x, offsetY - contentInsetY - sectionHeight) animated:YES];

これで、基本的にセクションの最初の行をスクロールして、セクションヘッダーの高さを差し引いて表示します。これにより、セクションヘッダーがピン留めされたヘッダーとともに必要な場所の上部に配置されるため、スクロールの方向は重要ではなくなります。セクションインセットではテストしませんでした。

1
nobody