UICollectionView
UICollectionViewFlowLayout
Swift,
iOS,
paging
More than 3 years have passed since last update.

概要

UICollectionviewをpagingEnable = trueでPage間のセルの位置ズレを発生させないでPagingする方法の紹介です。
スクリーンショット 2015-07-22 1.49.11.png
スクリーンショット 2015-07-22 1.49.14.png

現状の問題点

ページングしていくと、初期位置が少しずつずれていく(下図参照)。

image0.png
image1.png

制限事項

1セクションのみ対応です。

対応方法

UICollectionFlowLayoutクラスを継承したクラス内で毎回、layout処理を
行う。
処理の流れは以下
1)cellサイズを取得する。
2)表示ウインドウ内に収まるrow、column数を算出する。
3)x, y軸方向マージンを計算する。
x軸方向マージン = {コンテントサイズ.width - (cell幅 * column数) - (columnCont > 1 ? (columnCont - 1)*minimumLineSpacing}/2.0
y軸方向マージン = {コンテントサイズ.height - (cell高 * row数) - (rowCount > 1 ? (rowCount - 1) * minimumInteritemSpacing}/2.0

4)ページ毎indexを計算する。
index = セルindex%(rowCount*columnCount)
ページ内のrow,col番号 2行3列という感じで
row = index%rowCount
col = index/rowCount

5)配置位置を計算してCGRectでセットする。

6)ページング量を計算してorigin.xにセットする。

実装は以下

private func frameForItemAtIndexPath(indexPath: NSIndexPath) -> CGRect {
    // 1)
    let canvasSize = self.collectionView?.frame.size ?? CGSizeZero
    // 2)
    let rowCount =  floor((canvasSize.height - self.sectionInset.top - self.sectionInset.bottom) / (self.itemSize.height + self.minimumInteritemSpacing))
    let columnCont = floor((canvasSize.width - self.sectionInset.left - self.sectionInset.right) / (self.itemSize.width + self.minimumLineSpacing))
    // 3)
    let pageMarginX = (canvasSize.width - columnCont * self.itemSize.width - (columnCont > 1 ? (columnCont - 1) * self.minimumLineSpacing : 0)) / 2.0
    let pageMarginY = (canvasSize.height - rowCount * self.itemSize.height - (rowCount > 1 ? (rowCount - 1) * self.minimumInteritemSpacing : 0)) / 2.0
    // 4)
    let pageIndex = indexPath.row%Int(rowCount * columnCont)
    let row     = pageIndex % Int(rowCount)
    let column  = pageIndex / Int(rowCount)
    // 5)
    var cellFrame = CGRectZero
    cellFrame.origin.x  = pageMarginX + CGFloat(column) * (self.itemSize.width + self.minimumLineSpacing)
    cellFrame.origin.y  = pageMarginY + CGFloat(row) * (self.itemSize.height + self.minimumInteritemSpacing)
    cellFrame.size.width    = self.itemSize.width
    cellFrame.size.height   = self.itemSize.height
    // 6)
    if (self.scrollDirection == UICollectionViewScrollDirection.Horizontal) {
        let page = floor(CGFloat(indexPath.row)/(rowCount * columnCont))
        cellFrame.origin.x += canvasSize.width * page
    }

    return cellFrame
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
    let attr = super.layoutAttributesForItemAtIndexPath(indexPath)
    attr.frame = self.frameForItemAtIndexPath(indexPath)
    return attr
}

override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
    var attrs = [AnyObject]()
    if let originAttrs = super.layoutAttributesForElementsInRect(rect) {
        for var index = 0; index < self.collectionView?.numberOfItemsInSection(0); index++ {
            let indexPath = NSIndexPath(forRow: index, inSection: 0)
            let itemFrame = self.frameForItemAtIndexPath(indexPath)
            if CGRectIntersectsRect(itemFrame, rect) {
                let attr = self.layoutAttributesForItemAtIndexPath(indexPath)
                attrs.append(attr)
            }
        }
    }
    return attrs
}

ソースコードはこちら

参考サイト

UICollectionView align logic missing in horizontal paging scrollview