コードだけでTinderUIを作ってみた

# はじめに

ライブラリ（Koloda）を使用し、Tinder風のUIを作成しました
スワイプできるカードをコードで書いており、何か別の場面でも活かせることができるのではないかと考えたため共有します
なるべくコピペでビルドすることができるようにしましたので、是非試してみてください

誰かのお役に立てれば幸いです

## 完成図

![tinderUI.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982100/9f0c6159-8065-2282-3838-7c8b84d83f0a.gif)



## 準備

下記の記事を参考にライブラリのインストール、koladaViewを準拠したviewの作成まで行ってください
またviewcontrollerに、imageが入った配列(imageArray)を用意したください

https://qiita.com/panzee54/items/7501c9f51fdffcf98393

```swift
    private let imageArray = ["", "", ""]
```

自分で作成したのはこんな感じ

画像内にあるKolodaViewに関しては、横幅が端末の0.96倍、この横幅に対して高さを1.2倍に設定し
上下左右中央に配置しています

![スクリーンショット 2023-03-30 23.50.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982100/5bcfe064-fdfd-a918-9f00-2f6da005a1da.png)

```swift: ViewController
    @IBOutlet weak var kolodaView: KolodaView!
```

ここからスワイプできるカードを作成していきます

## コード

viewcontrollerにライブラリをimport

```swift: ViewController
import Koloda
```

delegate, datasourceを忘れずに

```swift: ViewController
override func viewDidLoad() {
        super.viewDidLoad()
        kolodaView.delegate = self
        kolodaView.dataSource = self
    }
```

Tinder風のカードの作成
※コピペで作成できます！

```swift: ViewController
extension ViewController: KolodaViewDataSource {
    
    // 表示内容
    func koloda(_ koloda: Koloda.KolodaView, viewForCardAt index: Int) -> UIView {
        // cardView 全体のview
        let userCardView = UIView(frame: koloda.bounds)
        userCardView.layer.cornerRadius = 8
        userCardView.clipsToBounds = true
        userCardView.contentMode = .scaleAspectFill
        
        
        // subview 相手の情報を表示するview
        let subView = UIView(frame: CGRect(x: 0, y: userCardView.frame.height * 0.72,
                                           width: userCardView.frame.width, height: userCardView.frame.height * 0.28))
        subView.backgroundColor = .black.withAlphaComponent(0.1)
        
        let imageView = UIImageView(frame: koloda.bounds)
        imageView.image = UIImage(named: imageArray[index])
        imageView.backgroundColor = .white
        imageView.contentMode = UIView.ContentMode.center
        
        // 名前・年齢を表示するStackView
        let infoStackView = UIStackView()
        let stackWidth:CGFloat = subView.frame.width * 0.7
        let stackHeight:CGFloat = subView.frame.height / 2
        
        // infoStackViewの大きさを決める
        let rect: CGRect = CGRect(x: 0, y: 0, width: stackWidth, height: stackHeight)
        
        // stackviewの位置を決定(名前、年齢、地域)
        infoStackView.frame = rect
        infoStackView.center = CGPoint(x: subView.frame.width / 2, y: userCardView.frame.height * 0.76)
        infoStackView.backgroundColor = .clear
        infoStackView.distribution = .fillEqually
        infoStackView.alignment = .fill
        infoStackView.spacing = 2.0
        
        // 名前ラベルの設定
        let nameStringLabel = UILabel()
        nameStringLabel.text = "相手の名前"
        nameStringLabel.textAlignment = .right
        nameStringLabel.textColor = .white
        nameStringLabel.backgroundColor = .clear
        nameStringLabel.frame = CGRect(x: 0, y: 0, width: infoStackView.frame.width / 2, height: infoStackView.frame.height)
        nameStringLabel.font = UIFont.boldSystemFont(ofSize: 23)
        
        infoStackView.addArrangedSubview(nameStringLabel)
        
        // 地域、年齢ラベル
        let ageRegionLabel = UILabel()
        ageRegionLabel.text = " 30歳・東京都"
        ageRegionLabel.textAlignment = .left
        ageRegionLabel.textColor = .white
        ageRegionLabel.backgroundColor = .clear
        ageRegionLabel.frame = CGRect(x: 0, y: 0, width: infoStackView.frame.width / 2, height: infoStackView.frame.height)
        ageRegionLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        
        infoStackView.addArrangedSubview(ageRegionLabel)
        
        // ユーザーのstatusを表示するStackView
        let userStatusStackView = UIStackView()
        let statusStackWidth:CGFloat = subView.frame.width * 0.8
        let statusStackHeight:CGFloat = subView.frame.height * 0.3
        
        let statusRect: CGRect = CGRect(x: 0, y: 0, width: statusStackWidth, height: statusStackHeight)
        
        // stackviewの位置を決定
        userStatusStackView.frame = statusRect
        userStatusStackView.center = CGPoint(x: userCardView.frame.width / 2, y: userCardView.frame.height * 0.86)
        userStatusStackView.backgroundColor = .clear
        userStatusStackView.distribution = .fillEqually
        userStatusStackView.alignment = .fill
        userStatusStackView.spacing = 5
        
        // 趣味ラベル
        let hobbyLabel = UILabel()
        hobbyLabel.text = "サッカー"
        hobbyLabel.textAlignment = .center
        hobbyLabel.textColor = .white
        hobbyLabel.backgroundColor = .systemPink
        hobbyLabel.frame = CGRect(x: 0, y: 0,
                                  width: userStatusStackView.frame.width / 3.3,
                                  height: userStatusStackView.frame.height)
        hobbyLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        hobbyLabel.layer.cornerRadius = 20
        hobbyLabel.clipsToBounds = true
        
        userStatusStackView.addArrangedSubview(hobbyLabel)
        
        // 性格のラベル
        let personalityLabel = UILabel()
        personalityLabel.text = "穏やか"
        personalityLabel.textAlignment = .center
        personalityLabel.textColor = .white
        personalityLabel.backgroundColor = .systemPink
        personalityLabel.frame = CGRect(x: 0, y: 0,
                                        width: userStatusStackView.frame.width / 3.3,
                                        height: userStatusStackView.frame.height)
        personalityLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        personalityLabel.layer.cornerRadius = 20
        personalityLabel.clipsToBounds = true
        
        userStatusStackView.addArrangedSubview(personalityLabel)
        
        // スタイルラベル
        let bodyStyleLabel = UILabel()
        bodyStyleLabel.text = "スリム"
        bodyStyleLabel.textAlignment = .center
        bodyStyleLabel.textColor = .white
        bodyStyleLabel.backgroundColor = .systemPink
        bodyStyleLabel.frame = CGRect(x: 0, y: 0, width: userStatusStackView.frame.width / 3.3,
                                      height: userStatusStackView.frame.height)
        bodyStyleLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        bodyStyleLabel.layer.cornerRadius = 20
        bodyStyleLabel.clipsToBounds = true
        
        userStatusStackView.addArrangedSubview(bodyStyleLabel)
        
        // viewを描画する
        userCardView.addSubview(imageView)
        userCardView.addSubview(subView)
        userCardView.addSubview(infoStackView)
        userCardView.addSubview(userStatusStackView)
        return userCardView
    }
    
    
    
    func kolodaNumberOfCards(_ koloda: KolodaView) -> Int {
        imageArray.count
    }
    
}
```

## 解説

#### UIViewの作成方法
userCardView を最初に定義しています
ここに後々作成していくStackViewなどをaddSubViewしていきます

```swift
        // cardView 全体のview
        let userCardView = UIView(frame: koloda.bounds)
        userCardView.layer.cornerRadius = 8
        userCardView.clipsToBounds = true
        userCardView.contentMode = .scaleAspectFill
```

subViewには相手の情報を表示します


```swift
        // subview 相手の情報を表示するview
        let subView = UIView(frame: CGRect(x: 0, y: userCardView.frame.height * 0.72,
                                           width: userCardView.frame.width, height: userCardView.frame.height * 0.28))
        subView.backgroundColor = .black.withAlphaComponent(0.1)
```

```
y: userCardView.frame.height * 0.72
height: userCardView.frame.height * 0.28
```
subViewをuserCardViewのheightの0.72の地点から、0.28分の大きさのviewと定義することができています

#### StackViewの作成方法

```swift
// ユーザーのstatusを表示するStackView
        let userStatusStackView = UIStackView()
        let statusStackWidth:CGFloat = subView.frame.width * 0.8
        let statusStackHeight:CGFloat = subView.frame.height * 0.3
        
        let statusRect: CGRect = CGRect(x: 0, y: 0, width: statusStackWidth, height: statusStackHeight)
        
        // stackviewの位置を決定
        userStatusStackView.frame = statusRect
        userStatusStackView.center = CGPoint(x: userCardView.frame.width / 2, y: userCardView.frame.height * 0.86)
        userStatusStackView.backgroundColor = .clear
        userStatusStackView.distribution = .fillEqually
        userStatusStackView.alignment = .fill
        userStatusStackView.spacing = 5
```


下記の部分で大きさを決定し、後述の.centerで位置を確定させています。
```swift
        let userStatusStackView = UIStackView()
        let statusStackWidth:CGFloat = subView.frame.width * 0.8
        let statusStackHeight:CGFloat = subView.frame.height * 0.3
        
        let statusRect: CGRect = CGRect(x: 0, y: 0, width: statusStackWidth, height: statusStackHeight)
        userStatusStackView.center = CGPoint(x: userCardView.frame.width / 2, y: userCardView.frame.height * 0.86)
```
ここにLabelであったり、自分の好きなパーツを入れてください！

## 自分がハマったポイント
- 作成したviewが表示されているのか、どこの位置にあるかわからない → backgroundColorに派手な色を入れるとわかりやすい
- StackViewは、addArrangedSubviewじゃないと反映されない
- コードで部品を作成する際は、必ず.frameを定義しないといけないこと
- 最後実際にaddSubViewする際は、階層が下のものから描画させるようにしないと、表示されない場合があること

## 最後に
codeでviewを描画するのは初めてでしたが、かなり勉強になりました。
次はautoLayoutを使ってコードでviewを書いてみたいと思います。
また、処理が冗長すぎるので、リファクタリングした際は、加筆していきたいです。

弊社では、経験の有無を問わず採用を行っています。
興味のある方は是非カジュアル面談しましょう！

https://jambo-support.com/recruit_engineer/