SH Lab の アプリ開発部屋

リリースしたアプリの告知とかサポートとか、技術的なお話とか、そんな感じでつぶやきます。

SwiftUI でYoutube動画プレイヤーを作ってみる

はじめに

SwiftUIでアプリを作っていても、どうしてもUIKitの資産を使いたい場面が出てくる事が多いですよね。UIKitむけの豊富なライブラリたちを使いたい場合などもたくさんあると思います。

f:id:hoshi0523:20201209224947p:plain

今回はまさに、UIKitむけのYoutubeプレイヤーライブラリである YoutubeKit をSwiftUIのアプリに組み込んでみたいと思います。

github.com

とりあえずの完成形

今回はこんな感じのものを作ってみたいと思います。

f:id:hoshi0523:20201209215547g:plain

GitHubはこちらです github.com

開発環境

参考書

SwiftUIの勉強には、この辺りの本が特におすすめです!

基礎から学ぶ SwiftUI

基礎から学ぶ SwiftUI

  • 作者:林晃
  • シーアンドアール研究所
Amazon

YoutubeKitを取り込む

今回はcocoapodsを使って、YoutubeKitを組み込んでいます。Swift Package Manager を使いたかったのですが、なぜかビルドが通りませんでした...

pod 'YoutubeKit'

UIViewRepresentable

UIViewRepresentableを使う事で、UIKitベースのViewをSwiftUIで利用可能なViewにラッピングできます。

今回は、YoutubeKitに含まれるYTSwiftyPlayerを、SwiftUIで利用可能なPlayerViewとしてラップします。

import SwiftUI
import YoutubeKit

struct PlayerView: UIViewRepresentable {
    
    typealias UIViewType = YTSwiftyPlayer
    
    // 初期化処理を記述.
    func makeUIView(context: Context) -> YTSwiftyPlayer {
        // 初期化処理・初期設定.
        let player = YTSwiftyPlayer()
        player.autoplay = true
        return player
    }
    
    // 更新処理を記述.
    func updateUIView(_ uiView: YTSwiftyPlayer, context: Context) {
        // 再生する動画の設定.
        uiView.setPlayerParameters([
            .playsInline(true),
            .videoID("TST0CURe3wM"),
        ])
        uiView.loadPlayer()
    }
}

makeUIViewには、対象のUIViewを生成・初期化するための処理を記述します。今回はYTSwiftyPlayerインスタンスの生成を行っています。

updateUIViewには、PlayerViewが更新されるたびに呼び出される処理を記述します。今回は、再生する動画の設定と読み込み処理を行っています。事前に自動再生を有効にしているので、読み込みが終わり次第再生が開始されます。

ContentViewでこのPlayerViewを読み込んで動きを確認してみます。

struct ContentView: View {
    var body: some View {
        PlayerView()
    }
}

出来上がりはこんな感じです。画面いっぱいにPlayerViewが広がって、固定で動画が再生されていますね。

f:id:hoshi0523:20201209220211g:plain

プレイヤーの大きさを調整

このままだとプレイヤーの見た目が残念なので、ContentViewに色々と処理を追加し、PlayerViewのサイズを調整してみます。

プレイヤーの幅はデバイス幅に合わせ、高さは16:9になるように算出します。

@State private var playerSize: CGSize = .zero

var body: some View {
    VStack {
        PlayerView()
            .frame( // 算出された値でサイズ指定.
                width: playerSize.width,
                height: playerSize.height
            )
    }
    // onAppearでサイズの計算を行う.
    .onAppear {
        // windowサイズからplayerサイズを算出.
        let frame = UIApplication.shared.windows.first?.frame ?? .zero
        
        // 縦横比が16:9になるように高さを調整.
        playerSize = CGSize(
            width: frame.width,
            height: frame.width / 16 * 9
        )
    }
}

それらしくなりましたね。

f:id:hoshi0523:20201209220736g:plain

動画の切り替え機能を追加する

今は再生する動画が固定になっていますが、これを切り替えるための機能を追加します。

まずは切り替え対象となる動画の種類をenumVideoとして定義します。

enum Video: String, CaseIterable {
    
    case violin
    case guitar
    case amaiyume
    
    var videoId: String {
        switch self {
        case .violin: return "TST0CURe3wM"
        case .guitar: return "QW2TfV20FXY"
        case .amaiyume: return "Y-ou8d-wMoI"
        }
    }
}

ContentViewには、選択された動画を保持する変数と動画切り替えのUIを追加します。

// 選択された動画を保持する変数を追加
@State private var selected: Video = .violin

var body: some View {
    VStack {
    
        // ~省略~

        // 動画切り替えのUIを追加.
        Picker("動画選択", selection: $selected) {
            ForEach(Video.allCases, id: \.self) { video in
                Text(video.rawValue.uppercased())
            }
        }
        // picker style 色々.
//        .pickerStyle(InlinePickerStyle())
        .pickerStyle(SegmentedPickerStyle())
//        .pickerStyle(MenuPickerStyle())
    }
    .onAppear {
        // ~省略~
    }
}

今回はPickerSegmentedPickerStyle()で表示しましたが、iOS14からstyleが追加されているので、それらを試してみるのも面白いかもしれません。

  • InlinePickerStyle()
  • MenuPickerStyle()

選択された動画を再生する

切り替えのUIは作ったので、実際に動画を切り替える処理を追加します。

PlayerViewには、選択された動画のVideo@Bindingの変数として定義し、その値を利用して動画を再生するように修正します。

// 選択されたVideoを受け取る@Binding変数を追加.
@Binding var video: Video

// ~省略~

func updateUIView(_ uiView: YTSwiftyPlayer, context: Context) {
    // 再生する動画の設定.
    uiView.setPlayerParameters([
        .playsInline(true),
        .videoID(video.videoId), // 選択された値のvideoIdを利用する.
    ])
    uiView.loadPlayer()
}

ContentViewでは、PlayerViewに選択されたVideoをbindingで渡します。

// コンストラクタに値を追加.
PlayerView(video: $selected)
    .frame(
        width: playerSize.width,
        height: playerSize.height
    )

これで完成です!出来上がりはこんな感じです!

f:id:hoshi0523:20201209215547g:plain

Youtube Data API

YoutubeKitには、他にもYoutube Data API を使うための機能など、他にもさまざまな機能が実装されています。今回は解説しませんが、これらと組み合わせる事でこんな感じのアプリも作れます!

また、今回はシミュレータを利用していますが、実機を利用すれば「ピクチャインピクチャ」を利用することもできるので、ぜひ試してみてください。

f:id:hoshi0523:20201209222031g:plain

まとめ

UIViewRepresentableを利用することで、UIKitの資産を使ったSwiftUIアプリを作る事ができます。先人たちによって作られた様々なライブラリやツールを使って、SwiftUIのアプリを作っていきましょう!

おまけ

今回使ったYoutube動画はこちらです。


夜に駆ける / YOASOBI【ヴァイオリンで歌ってみた】


A Metal Tribute to iPhone Ringtones || ToxicxEternity


上田麗奈「あまい夢」 MUSIC VIDEO