36583


CollectionViewを利用して水平スクロールを実現しております。  
セルの数に応じて、セルの配置を変化させる実装方法を教えてください。  
期待する動作は下記のとおりです。

■期待する動作  
セルが１個の際はそのセルを中央に配置し、セルが２個の際は各セルの中間を中央に配置する実装を検討しております。  
セルが３個以上の際は、セルの１個目を左端に配置し、スクロールできる状態にしたいと考えております。

■現状  
現状の実装ですと、セルが１個、２個の際は左に寄ってしまい中央に配置させることができておりません。  
参考までに現状のソースコード、Main.storyboardのキャプチャ、実行結果を添付いたします。

■ソースコード

[code]

    import UIKit
    
    class ViewController: UIViewController, UICollectionViewDataSource {
    
        //データの個数を返すメソッド
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
        {
            //下記の値cellsが可変のため、値が変化した際のセルの配置方法を変化させたい。
            return cells
        }
    
        //データを返すメソッド
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
        {
            //コレクションビューから識別子「TestCell」のセルを取得する。
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCell", for: indexPath) as! TestCollectionViewCell
            //セルの背景色をランダムに設定する。
            cell.backgroundColor = UIColor(red: CGFloat(drand48()),
                                       green: CGFloat(drand48()),
                                       blue: CGFloat(drand48()),
                                       alpha: 1.0)
            //セルのラベルに番号を設定する。
            cell.testLabel.text = String(indexPath.row + 1)
            return cell
        }
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    }
    
[/code]

■Main.storyboardのキャプチャ  
[![画像の説明をここに入力](https://i.stack.imgur.com/sMxH4.png)](https://i.stack.imgur.com/sMxH4.png)

■実行結果（セルが１個のとき）  
[![画像の説明をここに入力](https://i.stack.imgur.com/SEWHy.png)](https://i.stack.imgur.com/SEWHy.png)

■実行結果（セルが２個のとき）  
[![画像の説明をここに入力](https://i.stack.imgur.com/O9j5n.png)](https://i.stack.imgur.com/O9j5n.png)


CC BY-SA 3.0
2017-07-22T08:22:34.357
36582
2017-07-22T11:06:17.047
2017-07-22T09:19:31.527
13972
8489
question
swift ios swift3
collectionViewを利用した際のセルの配置方法について


`UICollectionView`と標準の`UICollectionViewFlowLayout`の組み合わせは、かなり多機能なので、適当なプロパティの値を設定すれば、ご説明のようなありがちなレイアウトは実現できるだろうと思ったのですが、リファレンスをちょっと探しただけではそれっぽいものは見つかりませんでした。

**uicollectionview center cells horizontally** 辺りで検索すると、本家Stack
Overflowにいくつかのスレッドが見つかりました。

[How to center horizontally UICollectionView
Cells?](https://stackoverflow.com/q/34267662/6541007)  
[How to center align the cells of a UICollectionView in
Swift3.0?](https://stackoverflow.com/q/40585822/6541007)

基本的にはみな`UICollectionViewDelegateFlowLayout`に定義されたメソッドを実装して、左右の余白を自前で計算することで実現しなさいと言うことのようです。

以下のコードは[その中の回答の一つ](https://stackoverflow.com/a/44497918/6541007)を焼き直したものです(変数名やコメントの修正だけで本質的には同じ内容)。

なお、このコードが動作するためには`UICollectionView`の`delegate`を`ViewController`のインスタンスに設定している必要があります。`dataSource`の方はstoryboardで設定を行われたものと思いますので、未接続でしたら、同じ手順で`delegate`の方も`ViewController`に接続しておいてください。

[code]

    //標準のFlowLayoutを使用するものとして、UICollectionViewDelegateFlowLayoutに適合させる
    class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
        //... 以下のメソッドの追加だけで、他の部分はそのままで良いはず
    
        //UICollectionViewDelegateFlowLayoutに定義されたメソッド
        //セクションごとに上下左右の余白を示すUIEdgeInsetsを返す
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    
            guard
                let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout,
                case let numItems = collectionView.numberOfItems(inSection: section), numItems > 0
            else {
                return .zero
            }
    
            //1セクション分の表示幅の計算(セルを可変サイズにする場合などは要修正)
            let minSpacing = flowLayout.minimumInteritemSpacing
            let itemWidth = flowLayout.itemSize.width
            let minSectionWidth = itemWidth * CGFloat(numItems) + minSpacing * CGFloat(numItems-1)
    
            //CollectionViewのコンテンツ領域の幅の計算
            let contentWidth = collectionView.frame.size.width - collectionView.contentInset.left - collectionView.contentInset.right
    
            //デフォルトのInsetsの取得
            var insets = flowLayout.sectionInset
            //セクションの表示幅がコンテンツ領域より小さい時には余白を計算
            if minSectionWidth < contentWidth {
                let paddingWidth = (contentWidth - minSectionWidth)/2
                insets.left = paddingWidth
                insets.right = paddingWidth
            }
            return insets
        }
    }
    
[/code]

注意点としては、

  * 設定項目の全てを全く同じに合わせたわけではないので、あちこち微修正が必要かもしれない  
(サイズなど、ご質問では表示されていない項目も多数ありますし…。)

  * 「セルが３個以上の際は、セルの１個目を左端に配置し、スクロールできる状態に」と明記されているが、このコードではセルが3個以上でも画面に収まる場合にはセンタリングして表示する

と言う作りになっています。

こちらの環境で適当に設定したプロジェクトでは期待通りに動いているのですが、上記したように、そちらのプロジェクトに合わせるためには若干修正が必要かもしれません。お試しください。


CC BY-SA 3.0
2017-07-22T09:41:54.087
36583
2017-07-22T11:06:17.047
2017-07-22T11:06:17.047
13972
13972
36582
answer