Posted at

【iOS】無限スクロールするUICollectionViewの作り方

More than 3 years have passed since last update.


はじめに

iOS Second Stage Advent Calendar 13日目の記事です。

今回で4記事目です。

宜しくお願いします。


本題

タイトル通り、無限スクロールするUICollectionViewです。

SampleInfinityCollectionView.gif

なんかGIFが載せられないので後日載せます。

プロジェクトは以下にあるので、興味ある方はみてください。

AdventCalendar2015/SampleInfinityCollectionView at master · ryokosuge/AdventCalendar2015


ちょっとした解説

UICollectionViewlayoutSubviews()でちょっと手を加えてます。


InfinityCollectionView.swift

import UIKit

class InfinityCollectionView: UICollectionView {

/// 表示するCellの数を増大させる比率
var factor: Int = 1
private var needsScrollToCenter: Bool = true

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
}

override func layoutSubviews() {
super.layoutSubviews()

layoutRecenter()

if contentSize.width > 0.0 && needsScrollToCenter {
var i: Int = 0
var x: CGFloat = 0.0
while i <= 0 {
let indexPath = NSIndexPath(forItem: i, inSection: 0)
guard let attribute = collectionViewLayout.layoutAttributesForItemAtIndexPath(indexPath) else {
break
}
var minimumInteritemSpacing: CGFloat = 0.0
if let delegateFlowLayout = delegate as? UICollectionViewDelegateFlowLayout {
minimumInteritemSpacing = delegateFlowLayout.collectionView?(self, layout: self.collectionViewLayout, minimumInteritemSpacingForSectionAtIndex: 0) ?? 0.0
} else if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
minimumInteritemSpacing = flowLayout.minimumInteritemSpacing
}
x += attribute.bounds.width - (minimumInteritemSpacing * 2)
i++
}
setContentOffset(CGPoint(x: contentOffset.x - x, y: 0), animated: false)
needsScrollToCenter = false
}

}
}

extension InfinityCollectionView {

private func layoutRecenter() {
let currentOffset = contentOffset
let contentWidth = contentSize.width
let centerOffsetX = contentWidth / 2.0

let distanceFromCenterX = fabs(currentOffset.x - centerOffsetX)

let allCellWidth = CGFloat(contentWidth / CGFloat(factor))
if distanceFromCenterX > allCellWidth {
let offset = CGFloat(fmodf(Float(currentOffset.x - centerOffsetX), Float(allCellWidth)))
contentOffset = CGPoint(x: centerOffsetX + offset, y: currentOffset.y)
}
}

}


要はscrollOffsetの値が真ん中以上だったらちょっと同じ要素のCellの位置までOffsetを戻して更新しているという感じです。

すごくシンプルな話なので、深く考えなくて大丈夫かと思います。


疑問

できれば普通にUICollectionViewを使うみたいにやりたかったのですが、UICollectionViewdelegateとかdataSourceとかをOverrideするやり方が Swiftだとわかりませんでした...。

どなたかやり方とかURLとか教えてもらえるとすごく嬉しいです。

オープンソース、見てみます。


終わりに

できることの知見を残しておきたいなと思い、色々サンプルを作っています。

結構サンプル作るの楽しいです。

おかげでGitHubにも草が生えてきました。

以上です。