From 5f4e94bf7c0c739af8313af0dcfe7559df7c0957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20D=C3=A9fago?= Date: Sun, 21 Apr 2024 19:05:53 +0200 Subject: [PATCH] Enable advanced Picture in Picture and Control Center integration for the chapter-supporting player (#836) --- Demo/Sources/Players/ChaptersPlayerView.swift | 100 +++++++++++------- Demo/Sources/Router/Router.swift | 2 +- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/Demo/Sources/Players/ChaptersPlayerView.swift b/Demo/Sources/Players/ChaptersPlayerView.swift index 53fe4df9a..411813cb3 100644 --- a/Demo/Sources/Players/ChaptersPlayerView.swift +++ b/Demo/Sources/Players/ChaptersPlayerView.swift @@ -8,7 +8,7 @@ import CoreMedia import PillarboxPlayer import SwiftUI -private struct ChapterView: View { +private struct ChapterCell: View { private static let width: CGFloat = 200 private static let durationFormatter: DateComponentsFormatter = { @@ -33,7 +33,7 @@ private struct ChapterView: View { .frame(width: Self.width, height: Self.width * 9 / 16) .clipShape(RoundedRectangle(cornerRadius: 5)) .saturation(isHighlighted ? 1 : 0) - .scaleEffect17(isHighlighted ? 1.05 : 1) + .scaleEffect17(isHighlighted ? 1.07 : 1) .animation(.defaultLinear, value: isHighlighted) } @@ -88,14 +88,13 @@ private struct ChapterView: View { } } -struct ChaptersPlayerView: View { - private let player = Player() - @State private var layout: PlaybackView.Layout = .minimized - @State private var chapters: [Chapter] = [] +private struct ChaptersList: View { + @ObservedObject var player: Player + @StateObject private var progressTracker = ProgressTracker(interval: .init(value: 1, timescale: 1)) - private var effectiveLayout: Binding { - !chapters.isEmpty ? $layout : .constant(.inline) + private var chapters: [Chapter] { + player.metadata.chapters } private var currentChapter: Chapter? { @@ -105,47 +104,72 @@ struct ChaptersPlayerView: View { } } - let media: Media + var body: some View { + ScrollView(.horizontal) { + chaptersList() + } + .scrollIndicators(.hidden) + .scrollClipDisabled17() + .bind(progressTracker, to: player) + ._debugBodyCounter(color: .purple) + } + + @ViewBuilder + private func chaptersList() -> some View { + HStack(spacing: 15) { + ForEach(chapters, id: \.timeRange) { chapter in + Button { + player.seek(to: chapter) + } label: { + ChapterCell(chapter: chapter, isHighlighted: chapter == currentChapter) + } + .buttonStyle(PlainButtonStyle()) + } + } + .padding(.horizontal) + } +} + +private struct MainView: View { + @ObservedObject var player: Player + @State private var layout: PlaybackView.Layout = .minimized + + private var chapters: [Chapter] { + player.metadata.chapters + } + + private var currentLayout: Binding { + !chapters.isEmpty ? $layout : .constant(.inline) + } var body: some View { VStack { - PlaybackView(player: player, layout: effectiveLayout) + PlaybackView(player: player, layout: currentLayout) .supportsPictureInPicture() - if layout != .maximized { - chaptersView() + if layout != .maximized, !chapters.isEmpty { + ChaptersList(player: player) } } .animation(.defaultLinear, values: layout, chapters) - .background(.black) - .bind(progressTracker, to: player) - .onReceive(player.$metadata, assign: \.chapters, to: $chapters) - .onAppear(perform: play) } +} - private func play() { - player.append(media.playerItem()) - player.play() +struct ChaptersPlayerView: View { + @StateObject private var model = PlayerViewModel.persisted ?? PlayerViewModel() + + let media: Media + + var body: some View { + MainView(player: model.player) + .enabledForInAppPictureInPicture(persisting: model) + .background(.black) + .onAppear(perform: play) + .tracked(name: "chapters-player") } - @ViewBuilder - private func chaptersView() -> some View { - if !chapters.isEmpty { - ScrollView(.horizontal) { - HStack(spacing: 15) { - ForEach(chapters, id: \.timeRange) { chapter in - Button { - player.seek(to: chapter) - } label: { - ChapterView(chapter: chapter, isHighlighted: chapter == currentChapter) - } - .buttonStyle(PlainButtonStyle()) - } - } - .padding(.horizontal) - } - .scrollIndicators(.hidden) - .scrollClipDisabled17() - } + private func play() { + model.media = media + model.play() } } diff --git a/Demo/Sources/Router/Router.swift b/Demo/Sources/Router/Router.swift index e5ff138ed..553dfe2e7 100644 --- a/Demo/Sources/Router/Router.swift +++ b/Demo/Sources/Router/Router.swift @@ -47,7 +47,7 @@ final class Router: ObservableObject { extension Router: PictureInPictureDelegate { func pictureInPictureWillStart() { switch presented { - case .player, .systemPlayer, .playlist, .multi: + case .player, .systemPlayer, .chaptersPlayer, .playlist, .multi: previousPresented = presented presented = nil case .inlineSystemPlayer: