7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UICollectionViewでscrollToItemAtIndexPathのcompletionがとれない

Last updated at Posted at 2015-11-18

要約

左右にスクロールできるUICollectionViewのスクロールアニメーション完了後に処理を行いたい

  • UIViewCATransaction でやってみるもうまくいかない
  • UIScrollViewDelegate利用する方法で対処できるが根本的解決ではない
  • 0.3秒の遅延実行やNSTimerの利用でも対処できるが、現実的ではない。

どなたか解決方法ご存知のかたいらっしゃいましたらコメントお願いいたします。 :bow:

本題

左右にスクロールできるUICollectionViewscrollToItemAtIndexPath:atScrollPosition:animated:のアニメーション完了後に処理を行いたい

サンプル

self.collectionView.scrollToItemAtIndexPath(lastItemIndexPath,
    atScrollPosition: UICollectionViewScrollPosition.Right,
    animated: true) // このアニメーション時間もできたら調整したい
completionBlock() // これをスクロールアニメーション終了後に行いたい
}

環境

  • Xcode 7.1
  • Swift 2.1
  • Mac OSX Yosemite

Pattern 1: UIView.animateWithDuration:animations:completion:(失敗)

UIView.animateWithDuration(10, animations: { () -> Void in
    self.collectionView.scrollToItemAtIndexPath(self.indexPathForLastItem,
        atScrollPosition: UICollectionViewScrollPosition.Right,
        animated: false)
    }) { (finished: Bool) -> Void in
        completionBlock()
}

結果:
10秒間collectionViewのアイテムが消えている状態が続いた後、およそ0.3秒でスクロールされる。
10秒待った後にcompletionBlockが実行される。

なお animatedtrueを指定した場合、
10秒指定は無視され、およそ0.3秒でスクロール。
completionBlockは即時実行される。

Pattern 2: CATransaction(失敗)

CATransaction.begin()
CATransaction.setAnimationDuration(10)
CATransaction.setCompletionBlock(completionBlock)
self.collectionView.scrollToItemAtIndexPath(self.indexPathForLastItem,
    atScrollPosition: UICollectionViewScrollPosition.Right,
    animated: false)
CATransaction.commit()

結果:
アニメーションなしで即スクロール。
completionBlockは即時実行される。

なお animatedtrueを指定した場合
10秒指定は無視され、およそ0.3秒でスクロール
completionBlockは即時実行される

Pattern 3: setContentOffset で指定(失敗)

結果:
同様にコントロールできず。
(めんどくさいんでサンプル割愛)

Pattern 4: もう0.3秒待っちゃえよ(一応成功)

self.collectionView.scrollToItemAtIndexPath(self.indexPathForLastItem,
    atScrollPosition: UICollectionViewScrollPosition.Right,
    animated: true)
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(0.3 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
    completionBlock()
}

結果:
目標の動作達成
いや、そりゃ動くけども...

Pattern 5: NSTimerでちょっとずつ動かす(多分成功)

いやだ :no_good:

Pattern 6: UIScrollViewDelegatescrollViewDidEndScrollingAnimation:を使う(成功)

ViewController側で
UIScrollViewDelegatescrollViewDidEndScrollingAnimation:を利用してcompletionBlockを実行

// Property
private var isScrollingToLast = false
private var scrollLastCompletion: (() -> Void)?

func scrollToLastItem() {
    self.collectionView.scrollToItemAtIndexPath(self.indexPathForLastItem, atScrollPosition: .Right, animated: true)
    let rightEdge = self.collectionView.contentOffset.x + self.collectionView.frame.size.width
    if rightEdge >= self.collectionView.contentSize.width {
        // Already scrolled to last item
        completionBlock()
    } else {
        self.isScrollingToLast = true
        self.scrollLastCompletion = completionBlock
    }
}

// MARK: - UIScrollViewDelegate
func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
    if self.isScrollingToLast {
        self.scrollLastCompletion?()
        self.isScrollingToLast = false
        self.scrollLastCompletion = nil
    }
}

すでに右端までスクロールしている場合scrollViewDidEndScrollingAnimation:が実行されないので計算が必要

一応完了後に実行できているので目標は達成しているものの、
ViewController側でコントロールしなければならないのでできれば避けたい。

参考

http://stackoverflow.com/questions/16597360/animation-duration-for-uicollectionview-selectitematindexpathanimatedscrollpos
http://stackoverflow.com/questions/4404745/change-the-speed-of-setcontentoffsetanimated

TODO

  • gifを載せる
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?