グリッド形式とリスト形式の表示切り替え


# はじめに
グリッド形式とリスト形式を切り替える画面は、たまに見ますよね。
今回は、CollectionViewを利用したサンプルをご紹介します。

## 完成イメージ
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/71694/90739f49-64bb-1ab6-a2dd-c9568988eea3.gif)

## 実装手順
1. グリッド形式のレイアウトを作成する
2. リスト形式のレイアウトを作成する
3. カスタムCollectionViewCellを作成する
4. レイアウトを作成する
5. セグメントコントロールで表示形式を切り替える

# 実装してみます

## 1. グリッド形式のレイアウトを作成する
2列毎にレイアウトします。

```swift:GridFlowLayout.swift
import UIKit

class GridFlowLayout: UICollectionViewFlowLayout {

    let itemHeight: CGFloat = 160

    override var itemSize: CGSize {
        set {
            self.itemSize = CGSizeMake(itemWidth(), itemHeight)
        }
        get {
            return CGSizeMake(itemWidth(), itemHeight)
        }
    }

    override init() {
        super.init()
        setupLayout()
    }

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

    func setupLayout() {
        minimumInteritemSpacing = 1
        minimumLineSpacing = 1
        scrollDirection = .Vertical
    }

    func itemWidth() -> CGFloat {
        return (CGRectGetWidth(collectionView!.frame)/2)-1
    }

    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint) -> CGPoint {
        return collectionView!.contentOffset
    }
}
```

## 2. リスト形式のレイアウトを作成する
グリッド形式のレイアウトとほぼ同じコードで冗長ですが、
1列毎にレイアウトします。

```swift:ListFlowLayout.swift
import UIKit

class ListFlowLayout: UICollectionViewFlowLayout {

    var itemHeight: CGFloat = 160

    override var itemSize: CGSize {
        set {
            self.itemSize = CGSizeMake(itemWidth(), itemHeight)
        }
        get {
            return CGSizeMake(itemWidth(), itemHeight)
        }
    }

    override init() {
        super.init()
        setupLayout()
    }

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

    func setupLayout() {
        minimumInteritemSpacing = 0
        minimumLineSpacing = 1
        scrollDirection = .Vertical
    }

    func itemWidth() -> CGFloat {
        return CGRectGetWidth(collectionView!.frame)
    }

    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint) -> CGPoint {
        return collectionView!.contentOffset
    }
}
```

## 3. カスタムCollectionViewCellを作成する
ImageViewがあるだけのセルを作ります。

```swift:
import UIKit

class ImageCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var imageView: UIImageView!

    static var identifier: String {
        get {
            return String(self)
        }
    }
}

```

## 4. レイアウトを作成する
SegmentedControlとCollectionViewを配置します。

なお、SegmentedControlは、
グリッド形式とリスト形式を切り替えるために利用します。

また、CollectionViewのみを利用し、TableViewは利用しません。

<img width="416" alt="スクリーンショット 2016-09-12 15.27.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/71694/c559cd44-43c6-6a1b-8d99-4b12bbeda6db.png">

## 5. セグメントコントロールで表示形式を切り替える

### 5.1. Viewのタイプを定義する
グリッド形式とリスト形式の2パターンを定義します。

```swift:ViewController.swift
enum ViewType {
    case grid
    case list
}
```

### 5.2. セグメントコントロールのイベントを受けて、レイアウトを切り替える
CollectionViewのsetCollectionViewLayoutメソッドを利用して、
所定のレイアウト(grid or list)に変更します。

アニメーションをつけると、それっぽくなります。

```
class ViewController: UIViewController {

    @IBOutlet weak var typeSegmentControl: UISegmentedControl!
    @IBOutlet weak var collectionView: UICollectionView!

    let gridFlowLayout = GridFlowLayout()
    let listFlowLayout = ListFlowLayout()

    var fileNames = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }

    private func setup() {

        //テストデータ
        for i in 1...10 {
            fileNames.append("\(i)")
        }

        collectionView.collectionViewLayout = gridFlowLayout
    }

    //セグメントコントロールで表示形式を切り替えます
    @IBAction func onDidTappedType(sender: UISegmentedControl) {

        switch sender.selectedSegmentIndex {
        case ViewType.grid.hashValue:
            changeView(gridFlowLayout)
            break

        case ViewType.list.hashValue:
            changeView(listFlowLayout)
            break

        default:
            break
        }
    }

    //Viewを切り替えます
    private func changeView(flowLayout: UICollectionViewFlowLayout) {

        UIView.animateWithDuration(0.2) { [weak self] () -> Void in
            self?.collectionView.collectionViewLayout.invalidateLayout()
            self?.collectionView.setCollectionViewLayout(flowLayout, animated: true)
        }
    }
}

extension ViewController: UICollectionViewDataSource {

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return fileNames.count
    }

    func collectionView(collectionView: UICollectionView,
                        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(ImageCollectionViewCell.identifier,
                                                                         forIndexPath: indexPath) as! ImageCollectionViewCell

        cell.imageView.image = UIImage(named: "\(fileNames[indexPath.row])"+".jpg")
        return cell
    }
}
```

# まとめ
グリッド形式とリスト形式を切り替えるには、
TableViewとCollectionViewを併用する方法もありますが、
シンプルなレイアウトなら、CollectionViewの列数を調整しても良さそうです。

## 参考
http://zappdesigntemplates.com/collectionview-flow-layout-from-grid-to-list-layout/