【Swift】CollectionViewでカード型デザインを実装しよう

今回はCollectionViewを使って横スクロールできるカード型デザインを実装しました！

##完成品
![cardDesignSample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/9ec71c2a-e95f-2441-b85b-e8f925b36c43.gif)

##1. Storyboardの編集
まずはStoryboardでViewController上にCollectionViewを配置します。画面いっぱいにパーツを広げ、Constrainsを適切に設定しておきます。
<img width="865" alt="スクリーンショット 2019-06-11 11.05.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/8e4542be-23c4-6286-c577-f263bd52c99d.png">

デフォルトでCollectionViewのスクロール方向はVerticalになっているので`Scroll Direction`を`Horizontal`に変更しておきましょう。
<img width="318" alt="スクリーンショット 2019-06-11 11.55.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/0c3a5bbb-c93e-f8f3-9f8a-3882816ee86e.png">


##2. CollectionViewのカスタムセルを作成
次にカスタムセルのxibファイルを作成します。
XCodeメニューのFile > New > File...とたどり、Cocoa Touch Classを選択します。
<img width="726" alt="スクリーンショット 2019-06-11 11.12.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/7fca66e3-ae5d-bd8a-fa88-42cd7a42b01f.png">

クラスの型はUICollectionViewCellにして、名前をつけます(ここではCollectionViewCellという名前をつけています)。この際に`Also create XIB file`にチェックをつけるのを忘れないように注意してください。

<img width="726" alt="スクリーンショット 2019-06-11 11.13.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/0adf9f1f-f173-72fd-dcc2-eaa20b258b52.png">


CollectionViewCell.xibファイルができたらパーツを適宜配置します。今回は背景のUIImageViewを画面いっぱいに広げ、その上にUILabelを2つ配置しました。
<img width="733" alt="スクリーンショット 2019-06-11 11.17.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/f8e1255c-24cd-a31b-e8c7-17e36e88003c.png">

UIImageViewのContentModeは以下のように'AspectFill`とし、`Clip to Bounds`にチェックを入れるのを忘れないように注意してください。
<img width="318" alt="スクリーンショット 2019-06-11 11.50.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/dd4171d0-87ff-e082-5b50-6f6323bd0f2f.png">

cellにはidentifierをつけておきます。ここでは`cell`としておきます。
<img width="313" alt="スクリーンショット 2019-06-11 11.22.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271820/310e7a87-6e5b-6081-0e4d-e8554e563760.png">

CollectionView.swiftでは配置したパーツの宣言を行い、背景の角を丸くしておきます。関連付けも忘れないようにしておきましょう。

```Swift
import UIKit

class CollectionViewCell: UICollectionViewCell {
    
    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var dateLabel: UILabel!
    @IBOutlet var backgroundImageView: UIImageView!

    override func awakeFromNib() {
        super.awakeFromNib()
        
        backgroundImageView.layer.cornerRadius = 12    
    }
}
```

##3. ViewControllerの編集
初めにViewDidLoad()内で`register(_:forCellWithReuseIdentifier:)`メソッドを使ってCollectionViewCellの登録を行います。第一引数にUINibオブジェクトを渡し、第二引数では先ほどcellにつけた`cell`というidentifierを渡しています。UINibの`nibName`はCollectionViewのxibファイル名と一致していることを確認してください。

```Swift
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet var horizontalCollectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let nib = UINib(nibName: "CollectionViewCell", bundle: .main)
        horizontalCollectionView.register(nib, forCellWithReuseIdentifier: "cell")
    }  
}
```

次にViewControllerに`UICollectionViewDelegate`,`UICollectionViewDataSource`,`UICollectionViewDelegateFlowLayout`プロトコルを実装します。今回は以下の4つのメソッドを実装しました。

コレクションビューに配置するセルの個数
`func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {}`1番目のメソッドではphotoArrayの中に入っている写真の個数だけセルを返し、3番目のメソッドでセルを再利用しているところがポイントです。

1. セルの中身
`func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {}`

2. セル同士の間隔
`func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {}`

3. セルのサイズ
`func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {}`

4. 余白の調整
`func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {}`


全体のコードは以下の通りです。
ViewController.swift

```Swift
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet var horizontalCollectionView: UICollectionView!
    
    var viewWidth: CGFloat!
    var viewHeight: CGFloat!
    var cellWidth: CGFloat!
    var cellHeight: CGFloat!
    var cellOffset: CGFloat!
    var navHeight: CGFloat!
    
    var photoArray = ["ny","italy","australia","france"]
    var titleArray = ["ニューヨーク","イタリア","オーストラリア","フランス"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        viewWidth = view.frame.width
        viewHeight = view.frame.height
        navHeight = self.navigationController?.navigationBar.frame.size.height
        
        horizontalCollectionView.delegate = self
        horizontalCollectionView.dataSource = self
        
        let nib = UINib(nibName: "CollectionViewCell", bundle: .main)
        horizontalCollectionView.register(nib, forCellWithReuseIdentifier: "cell")
        
        self.navigationController!.navigationBar.setBackgroundImage(UIImage(), for: .default)
        self.navigationController!.navigationBar.shadowImage = UIImage()
        self.navigationController?.navigationBar.tintColor = .white
        self.navigationController?.navigationBar.titleTextAttributes = [
            .foregroundColor: UIColor.white
        ]
        
    }
    
}

extension ViewController: UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return photoArray.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
        cell.backgroundColor = UIColor.white
        cell.layer.cornerRadius = 12
        cell.layer.shadowOpacity = 0.4
        cell.layer.shadowRadius = 12
        cell.layer.shadowColor = UIColor.black.cgColor
        cell.layer.shadowOffset = CGSize(width: 8, height: 8)
        cell.layer.masksToBounds = false
        cell.titleLabel.text = "\(titleArray[indexPath.row])への旅"
        cell.dateLabel.text = "06/08~06/15"
        cell.backgroundImageView.image = UIImage(named: photoArray[indexPath.row])
        return cell
        
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 24
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        cellWidth = viewWidth-75
        cellHeight = viewHeight-300
        cellOffset = viewWidth-cellWidth
        return CGSize(width: cellWidth, height: cellHeight)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: -navHeight,left: cellOffset/2,bottom: 0,right: cellOffset/2)
    }
    
}
```

CollectionView.swift

```Swift
import UIKit

class CollectionViewCell: UICollectionViewCell {
    
    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var dateLabel: UILabel!
    @IBOutlet var backgroundImageView: UIImageView!

    override func awakeFromNib() {
        super.awakeFromNib()
        
        backgroundImageView.layer.cornerRadius = 12    
    }
}
```