【Swift4】Tinder風UI(ユーザーインターフェース)の作成方法

## はじめに
どうも、<a href = "https://togamin.com/">とがみん</a>です。現在、セブ島にあるプログラミングと英語の両方を学ぶことができるNexSeedという学校でエンジニアインターンをしています。 

留学していた時の記事はこちらです。

[>NexSeedでのセブ島エンジニア留学を振り返ってみる。](https://togamin.com/2018/08/04/wp180804/)

主に、Swiftを使ったiOSアプリ開発コースのティーチングアシスタントをしていて、 

今は生徒たちが、それぞれiOSアプリのアイデアを考えて、その開発を頑張っているところです。

自分は生徒たちが実装したい機能、実装にあたって詰まりそうな機能を先回りして実装し、ヒントを出したり、その機能の実装方法を授業として解説したりしています。

そんな中で、マッチングアプリでお馴染みの「Tinder風のUI」が結構人気で、その実装方法を考えたので、この記事で紹介したいと思います。

## Tinder風のUIのデモ
以下が今回作成した、Tinder風のUIのデモです。スワイプした方向にカードが飛んでいき、スワイプする方向によって処理を変えることができます。リセットボタンを押すと、元に戻ります。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">【Swift4】Tinder風のUIを作成しました。 <a href="https://t.co/OibopsoNFr">pic.twitter.com/OibopsoNFr</a></p>&mdash; とがみん@セブなう。本質を追求するブロガー (@togaminnnn) <a href="https://twitter.com/togaminnnn/status/1047624879992856577?ref_src=twsrc%5Etfw">2018年10月3日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## Tinder風UI実装の仕方
### 1.UIの配置について
画面には、2つのUIViewにそれぞれUIImageViewが追加されています。複数枚カードがあるように見えますが、「frontCard」と「backCard」の2枚だけです。

手前にある「frontCard」にのみ、「Pan Gesture Recognitionnizer」がついています。

![Tinder01.jpg](https://qiita-image-store.s3.amazonaws.com/0/295037/4457e43b-14e3-55e6-ee74-36dd18826fe5.jpeg)

手前の「frontCard」のみが動き、「backCard」は静止したままで、スワイプされた時「frontCard」がどこかへ行ってしまうように見えますが、実際は行った後すぐに元の位置に戻っています。中の画像の情報のみが移動していく仕組みになっています。その処理について、詳しく考えていきます。

### 2.スワイプした時の処理について
スワイプした時に、スワイプした距離に応じて、x座標を動かし、画面の大きさに対して、スワイプした割合に応じて、カードを傾ける処理を書きます。

```swift
//手前のカードをスワイプした分だけ、横に動かし、動かした距離に応じて回転させる。
//「point.x」に0.1をかけているのは、スワイプした時に動きすぎないようにするため。

//スワイプした時にx座標だけ動かす。
swipeCard.center = CGPoint(x: swipeCard.center.x + point.x * 0.1,y:swipeCard.center.y)
//Max45度の傾き。スワイプした距離に応じて傾きを変える
swipeCard.transform = CGAffineTransform(rotationAngle: swipeDistanceX/(view.frame.width/2) * -0.785)
```

スワイプした時の処理に関しては、スワイプして指が離れた時点で、小さくスワイプしていた場合と、大きくスワイプした場合で分けます。



```swift
//スワイプの指が離れたときの処理.
if sender.state == UIGestureRecognizerState.ended{
    //処理を記入
    //左に大きく振れた時
    if swipeCard.center.x < self.screenWidth/5 {
        UIView.animate(withDuration: 0, animations: {
            
        })
    }
    //右に大きく振れた時
    else if swipeCard.center.x > self.screenWidth - self.screenWidth/5{
        UIView.animate(withDuration: 0, animations: {
            
        })
    }
    //小さく振れた時
    else {
        UIView.animate(withDuration: 0.2, animations: {
            
        })
    }
}
```

カードを小さくスワイプした時は、元の位置に戻します。

```swift
//小さく振れた時、カードを元の位置に戻す
swipeCard.center = CGPoint(x: self.cardCenter.x,y:self.cardCenter.y)
swipeCard.transform = .identity
```

カードを大きくスワイプした時は、以下の手順で処理を実装します。以下には左側にスワイプした時の処理しか書いていませんが、右側にスワイプした場合の処理も同様に実装することができます。

- スワイプした方向にカードを飛ばす。

  ```swift
  swipeCard.center = CGPoint(x: self.cardCenter.x + self.screenWidth,y:self.cardCenter.y)
  ```

- 飛ばしたカードの情報を保持する(処理する)。

- ```swift
  self.leftInfo.append(self.imageList[self.cardNum])
  print("左スワイプデータ：",self.leftInfo)
  ```

- 飛ばしたカードを透明にする。

- ```swift
  swipeCard.alpha = 0
  ```

- 透明にした後、カードを元の位置に戻す。この時、「frontCard」と「backCard」の両方が画面に存在するが、「frontCard」は透明なので、「backCard」のみが見えている状態になります。

- ```swift
  swipeCard.center = CGPoint(x: self.cardCenter.x,y:self.cardCenter.y)
  swipeCard.transform = .identity
  ```

- 「frontCard」カードに、「backCard」の情報を入れます。同じデザインのカードが2枚ある状態で、手前は透明です。

  ```swift
  self.frontCardImage.image = self.backCardImage.image
  ```

- 「frontCard」カードを見えるようにする。

  ```swift
  swipeCard.alpha = 1
  ```

- 最後に「backCard」に次の要素の情報を入れます。この時、最後から2枚目の場合と最後のカードの場合は場合分が必要になります。「backCard」に入れる情報がなくなり、「Out of range」のエラーがでてしまうので。

  ```swift
  //(1)カードが残り2枚になるまで
  if self.cardNum < self.imageCount - 2{
      self.cardNum += 1
      self.nextCard += 1
      self.backCardImage.image = UIImage(named:self.imageList[self.nextCard])
  //(2)カードが残り2枚の時
  }else if self.cardNum == self.imageCount - 2{
        self.cardNum += 1
        self.backCard.alpha = 0
  //カードが残り1枚の時
  }else if self.cardNum == self.imageCount - 1{
        self.frontCard.alpha = 0
        print("カードがなくなりました。")
  }
  ```

![Tinder02.jpg](https://qiita-image-store.s3.amazonaws.com/0/295037/bed1a819-7c21-2bb6-b266-c6d5e6e980ad.jpeg)

### 3.リセットボタンについて

リセットボタンを押した後は、以下のように、初期値に戻せばリセット完了です。

```swift
rightInfo = []
leftInfo = []
cardNum = 0
nextCard = 1
frontCardImage.image = UIImage(named:imageList[cardNum])
backCardImage.image = UIImage(named:imageList[nextCard])
frontCard.alpha = 1
backCard.alpha = 1
```

## GitHub

ソースコードは以下に挙げているので参考にしながら実装してみてください。

https://github.com/togamin/TinderUISample2.git


## 最後に

TinderのUIを実装しました。実装の際は結構頭を使うので、プログラミングの良い練習になるかと思います。

是非実装してみてください。

また、個人ブログ[「とがみんブログ」](https://togamin.com/)も運営しているので、よければチェックしてみてください。

就職活動、心理学、脳科学、量子力学、とがみんの経験等、変なこと書いてます。