SwiftUI | 全画面表示した動画をドラッグジェスチャーで前画面に戻す方法

音声・動画

SwiftUIで全画面表示した動画をドラッグジェスチャーで前画面に戻す説明する。

Swift 5.7 / Xcode 14.0 / iOS 16.0

結論

環境変数dismissを定義してDragGesture()でdismiss()を実行する。

具体例

NavigationLinkから動画を全画面再生し、動画再生画面で下ドラッグするとNavigationViewの親に戻るというAppを作成する。

コード

こちらの記事SwiftUI | Picture in Picture (PiP)のコードをベースとする。

  1. 環境変数dismissを定義する。
  2. 動画を全画面表示するためにNavigationBarのBackボタンを非表示にしSafeAreaを無視する設定にする。
  3. 下にドラッグしたときにdismiss()によりViewを破棄しNavigationBarのBackボタンを表示する。
import SwiftUI
import AVKit
import Combine

let my動画 = AVModel(title: "地球の動画",
                    url: Bundle.main.url(forResource: "earth",
                                         withExtension: "mp4")!)

struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink("▶ 動画を視聴する", destination: 動画ビュー(動画: my動画))
        }
    }
}

struct 動画ビュー: View {
    @Environment(\.dismiss) private var dismiss               // ? 1
    @State var backButtonHidden: Bool = true
    @StateObject private var viewModel = AVViewModel()
    let 動画: AVModel
    
    var body: some View {
        AVView(avViewModel: viewModel)
            .onAppear {
                viewModel.avModel = 動画
                viewModel.player.play()
            }
            .navigationBarBackButtonHidden(backButtonHidden)  // ? 2
            .edgesIgnoringSafeArea(.all)                      // ? 2
            .gesture(
                DragGesture()
                    .onEnded { value in
                        if value.translation.height > 10 {
                            viewModel.player.pause()
                            dismiss()                         // ? 3
                            backButtonHidden = false          // ? 3
                        }
                    }
            )
    }
}

// View
struct AVView: UIViewControllerRepresentable {
    @ObservedObject var avViewModel: AVViewModel
    
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let vc = AVPlayerViewController()
        vc.player = avViewModel.player
        vc.canStartPictureInPictureAutomaticallyFromInline = true
        return vc
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController,
                                context: Context) { }
    
}

// ViewModel
class AVViewModel: ObservableObject {
    @Published var avModel: AVModel?
    let player = AVPlayer()
    private var cancellable: AnyCancellable?
    
    init() {
        setAudioSessionCategoryToPlayback()
        cancellable = $avModel.sink(receiveValue: {
            guard let model = $0 else { return }
            self.player.replaceCurrentItem(with: AVPlayerItem(url: model.url))
        })
    }
    
    func setAudioSessionCategoryToPlayback() {
        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setCategory(.playback)
        } catch {
           print("Setting category to AVAudioSessionCategoryPlayback failed.")
        }
    }
}

// Model
struct AVModel {
    let title: String
    let url: URL
}

まとめ

SwiftUIで全画面表示した動画をドラッグジェスチャーで前画面に戻す説明した。

コメント

タイトルとURLをコピーしました