要約
左右にスクロールできるUICollectionView
のスクロールアニメーション完了後に処理を行いたい
-
UIView
やCATransaction
でやってみるもうまくいかない -
UIScrollViewDelegate
利用する方法で対処できるが根本的解決ではない - 0.3秒の遅延実行や
NSTimer
の利用でも対処できるが、現実的ではない。
どなたか解決方法ご存知のかたいらっしゃいましたらコメントお願いいたします。
本題
左右にスクロールできるUICollectionView
のscrollToItemAtIndexPath: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
が実行される。
なお animated
にtrue
を指定した場合、
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
は即時実行される。
なお animated
にtrue
を指定した場合
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
でちょっとずつ動かす(多分成功)
いやだ
Pattern 6: UIScrollViewDelegate
のscrollViewDidEndScrollingAnimation:
を使う(成功)
ViewController側で
UIScrollViewDelegate
のscrollViewDidEndScrollingAnimation:
を利用して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を載せる