SwiftUIで著名アプリのUIをトレース ~Spotify編~

iOS14とXcode12がリリースされ、SF Symbols2が使えるようになったので著名なアプリのUIをトレースするのが簡単になったなぁと思いレイアウトの勉強がてらSpotifyの再生画面をトレースしてみました。

## 完成品
![Simulator Screen Shot - iPhone 8 - 2020-09-24 at 18.53.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147358/6aa67765-1327-6fba-5fc3-560d4988205c.png)
厳密には背景がアートワークのグラデーションが薄っすらかかっていますが割愛

### もしもSpotifyがライトモードだったら
![Simulator Screen Shot - iPhone 8 - 2020-09-24 at 18.53.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147358/a2e5fbdd-a427-ed81-49e1-9e32a875a551.png)

SwiftUIが自動でライトモードとダークモード対応してくれるので、こんなもしかしたらな画面も生成できました。
まぁライトモードでこれだと完全トレースとは言えないんですが。。。

### コード

```swift:PlayerView.swift

import SwiftUI

struct PlayerView: View {
    @Binding var isPresent: Bool
    @State var seekValue = 0.7
    @State var isPlaying = false
    
    var body: some View {
        VStack(spacing: 24) {
            HStack {
                Button(action: {
                    self.isPresent = false
                }, label: {
                    Image(systemName: "chevron.down")
                        .foregroundColor(.primary)
                })
                Spacer()
                Text("834.194")
                    .font(.caption)
                    .fontWeight(.semibold)
                Spacer()
                Image(systemName: "ellipsis")
            }
            Image("artwork")
                .resizable()
                .imageScale(.large)
                .scaledToFit()
            TitleAndSeekControl(seekValue: $seekValue)
            PlayerControl()
            ActionControl()
        }
        .padding(.horizontal)
    }
}

private struct TitleAndSeekControl: View {
    @Binding var seekValue: Double
    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            Text("忘れられないの")
                .font(.title2)
                .fontWeight(.semibold)
            Text("サカナクション")
                .foregroundColor(.secondary)
            Slider(value: $seekValue)
                .accentColor(.secondary)
            HStack {
                Text("2:31")
                Spacer()
                Text("3:43")
            }
            .foregroundColor(.secondary)
            .font(.caption)
        }
    }
}

private struct PlayerControl: View {
    var body: some View {
        HStack {
            Image(systemName: "heart")
            Spacer()
            HStack(spacing: 32) {
                Image(systemName: "backward.end.fill")
                    .font(.title2)
                Image(systemName: "play.circle.fill")
                    .font(.system(size: 48))
                Image(systemName: "forward.end.fill")
                    .font(.title2)
            }

            Spacer()
            Image(systemName: "minus.circle")
        }
        .imageScale(.large)
    }
}

private struct ActionControl: View {
    var body: some View {
        HStack {
            Image(systemName: "tv.and.hifispeaker.fill")
            Spacer()
            Image(systemName: "square.and.arrow.up")
        }
        .imageScale(.medium)
    }
}

struct PlayerView_Previews: PreviewProvider {
    @State static var isPresent: Bool = true
    static var previews: some View {
        PlayerView(isPresent: $isPresent)
    }
}

```

それとなくStackでグルーピングされてそうな要素を個別のViewに書き出しています。
Viewモディファイアの勉強に既存のアプリのトレースはなかなかいいです。
あくまで見た目上のトレースなので実用するにはバインディングをもうちょっと整理しないといけませんが。

## 非デザイナーに嬉しいSF Symbols
僕は画面のオブジェクトを自分でデザインとかできないのでSF Symbolsは相当助かります。
SF Symbolsはこんなのもあるの?ってくらい追加されているので、iOSアプリ開発ではFontAwesomeからSF Symbolsに移行していくのではないでしょうか。