web-dev-qa-db-ja.com

特定のインデックスパスでUICollectionViewを起動します

現在、各セルがフルスクリーンである水平ページングを行うコレクションビューがあります。私がしたいのは、コレクションビューが表示されたときに特定のインデックスで開始することです。

現在、私はscrollToItemAtIndexPath:atScrollPosition:animated:を使用しており、animatedをNOに設定していますが、特定のアイテムにスクロールする前に最初のインデックスを最初にロードします。また、このメソッドはViewDidAppearでしか使用できないため、最初のセルを表示してから、表示したいセルに点滅します。スクロールが完了するまでコレクションビューを非表示にすることでこれを非表示にしますが、理想的ではないようです。

私が説明した方法以外にこれを行うより良い方法はありますか?

ありがとう!

40
Zack

そこで、UICollectionViewDelegateメソッドと1回限りのBoolを使用して、これを別の方法で解決しました。

スイフト2:

var onceOnly = false

internal func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
    if !onceOnly {
        let indexToScrollTo = NSIndexPath(forRow: row, inSection: section)
        self.problemListCollectionView.scrollToItemAtIndexPath(indexToScrollTo, atScrollPosition: .Left, animated: false)
        onceOnly = true
    }

}

スウィフト3:

  var onceOnly = false

  internal func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if !onceOnly {
      let indexToScrollTo = IndexPath(item: row, section: section)
      self.problemListCollectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
      onceOnly = true
    }
  }

このコードは、アニメーションが発生する前に実行されるため(実際にこの時点までロードされます)、viewDidAppearを呼び出すよりも優れており、viewWillAppearで成功しませんでした。

53
sschale

この問題を解決するために、 greenhouse 回答を部分的に使用しました。

/// Edit
var startIndex: Int! = 0

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    collectionView.setNeedsLayout()
    collectionView.layoutIfNeeded()

    collectionView.scrollToItemAtIndexPath(
        NSIndexPath(forItem: 0, inSection: startIndex),
        atScrollPosition: .None,
        animated: false)
 }

問題は、間違ったcollectionViewサイズにあるようです。レイアウトを設定した後、scrollToItemAtIndexPathは必要な結果を生成します。

また、この問題はCollection ViewUIViewController内で使用されます。

16
victor.vasilica

残念ながら、これらの既存の回答のすべてが、少なくとも部分的に間違っているか、尋ねられている正確な質問に答えていません。私は、これらの回答のいずれにも助けられなかった同僚とこの問題を解決しました。

必要なことは、アニメーションなしのコンテンツオフセットを正しいコンテンツオフセットに設定してから、リロードデータを呼び出すことだけです。 (または、まだ読み込まれていない場合はreloadData呼び出しをスキップします。)最初のセルを作成したくない場合は、viewDidLoadでこれを行う必要があります。

この答えは、コレクションビューが水平にスクロールし、セルのサイズがビューと同じサイズであると仮定していますが、垂直にスクロールしたい場合やセルが異なるサイズである場合、概念は同じです。また、CollectionViewに複数のセクションがある場合は、コンテンツオフセットを計算するためにもう少し計算する必要がありますが、概念は同じです。

func viewDidLoad() {
    super.viewDidLoad()
    let pageSize = self.view.bounds.size
    let contentOffset = CGPoint(x: pageSize.width * self.items.count, y: 0)
    self.collectionView.setContentOffset(contentOffset, animated: false)
}
5
Logan Shire

Swift 3.0はテスト済みで動作します。

var onceOnly = false
    internal func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if !onceOnly {
            //set the row and section you need.
            let indexToScrollTo = IndexPath(row: 1, section: indexPath.section)
            self.fotmCollectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
            onceOnly = true
        }
    }
5
kemicofa

これは私にとってはうまくいくようです:

- (void)viewDidLayoutSubviews
{     
   [self.collectionView layoutIfNeeded];
   NSArray *visibleItems = [self.collectionView indexPathsForVisibleItems];
   NSIndexPath *currentItem = [visibleItems objectAtIndex:0];
   NSIndexPath *nextItem = [NSIndexPath indexPathForItem:someInt inSection:currentItem.section];

   [self.collectionView scrollToItemAtIndexPath:nextItem atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}
3
greenhouse

IndexPathを最初のVC=からDidSelectItemAtIndexPathメソッドのコレクションビューに渡します。コレクションビューのviewDidLoadで、メソッドscrollToItemAtIndexPath:atScrollPosition:animated: Set animated to NO and atScrollPosition to UICollectionViewScrollPositionNone。このように:

[self.collectionView scrollToItemAtIndexPath:self.indexPathFromVC atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
2
Stephen Paul

他の人に触発されたシンプルなソリューション:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    DispatchQueue.main.async {
        self.collectionView.scrollToItem(at: lastIndexPath, section: 0), at: .centeredHorizontally, animated: false)
    }
}

コードをDispatchQueue.main.asyncブロック内に配置すると機能します。

2
Andres Wang

ここに私のために働いたものがあります(UICollectionViewControllerクラスで):

private var didLayoutFlag: Bool = false

public override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let collectionView = self.collectionView {
        if !self.didLayoutFlag {
            collectionView.scrollToItemAtIndexPath(self.viewModel.initialIndexPath, atScrollPosition: .None, animated: false)
            self.didLayoutFlag = true
        }
    }
}
1
Nikolay Spassov

ちょっと私はObjective Cで解決しました。これはあなたにとって役に立つと思います。 Objective CをSwiftにも変換できます。ここに私のコードがあります:

**私の場合、[オン]ボタンをクリックすると、特定のインデックス、つまり3が有効になります**

垂直用

 - (IBAction)Click:(id *)sender {
    NSInteger index=3;
    CGFloat pageHeight = self.collectionView.frame.size.height;
    CGPoint scrollTo = CGPointMake(0, pageHeight * index);
    [self.collectionView setContentOffset:scrollTo animated:YES];
}

水平用

 - (IBAction)Click:(id *)sender {
    NSInteger index=3;
    CGFloat pageWidth = self.collectionView.frame.size.width;
    CGPoint scrollTo = CGPointMake(pageWidth * index, 0);
    [self.collectionView setContentOffset:scrollTo animated:YES];
}

それがあなたに役立つことを願っています。

0
user2856484

この同じ問題を抱えてここに来て、私の場合、この問題はストーリーボードのViewControllerが「フリーフォーム」サイズに設定されていることが原因であることがわかりました。

ストーリーボードのサイズが不明な場合、ビューが最初に読み込まれるときにviewWillLayoutSubviewsが呼び出されて正しいサイズが計算されると思います。 (ストーリーボードのviewControllerのサイズを「フリーフォーム」に設定したため、collectionView内の長いtableViewの多くのセルを表示/編集できるように非常に背の高いものにできました)。

私は、victor.vasilicaと温室のアプローチがre: 'scrollToRow'コマンドをviewWillLayoutSubviewsに入れることで問題を修正するために完全に機能することを発見しました。

ただし、ストーリーボードの「固定」サイズでVCを再度作成すると、問題はすぐになくなり、viewWillAppearから初期セルを設定できるようになりました。異なっていますが、これは私の状況で何が起こっているのかを理解するのに役立ち、私の答えがこの問題を他の人に知らせるのに役立つことを願っています。

0
Natalia