Skip to content

Commit

Permalink
Revisit menu API (#570)
Browse files Browse the repository at this point in the history
Co-authored-by: Walid Kayhal <3347810+waliid@users.noreply.github.com>
  • Loading branch information
defagos and waliid committed Sep 15, 2023
1 parent 2242d81 commit de8ba99
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 148 deletions.
25 changes: 20 additions & 5 deletions Demo/Sources/Players/PlaybackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,12 @@ private struct TimeBar: View {
HStack(spacing: 20) {
TimeSlider(player: player, progressTracker: progressTracker)
LiveLabel(player: player, progressTracker: progressTracker)
SettingsMenu(player: player)
.padding(.vertical, 12)
FullScreenButton(layout: $layout)
.padding(.vertical, 12)

Group {
settingsMenu()
FullScreenButton(layout: $layout)
}
.padding(.vertical, 12)
}
}
.frame(height: 44)
Expand All @@ -396,6 +398,19 @@ private struct TimeBar: View {
.aspectRatio(contentMode: .fit)
}
}

@ViewBuilder
private func settingsMenu() -> some View {
Menu {
player.standardSettingMenu()
} label: {
Image(systemName: "ellipsis.circle")
.resizable()
.tint(.white)
.aspectRatio(contentMode: .fit)
}
.menuOrder(.fixed)
}
}

// Behavior: h-exp, v-hug
Expand Down Expand Up @@ -543,7 +558,7 @@ struct PlaybackView: View {

struct PlaybackView_Previews: PreviewProvider {
static var previews: some View {
PlaybackView(player: Player())
PlaybackView(player: Player(item: Media(from: URLTemplate.onDemandVideoLocalHLS).playerItem()))
.background(.black)
.previewLayout(.fixed(width: 320, height: 180))
}
Expand Down
65 changes: 0 additions & 65 deletions Sources/Player/UserInterface/MediaSelectionMenu.swift

This file was deleted.

52 changes: 0 additions & 52 deletions Sources/Player/UserInterface/PlaybackSpeedMenu.swift

This file was deleted.

123 changes: 97 additions & 26 deletions Sources/Player/UserInterface/SettingsMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,115 @@
// License information is available from the LICENSE file.
//

import AVFoundation
import SwiftUI

/// A standard settings menu mimicking Apple player menu.
///
/// Behavior: h-exp, v-exp
@available(iOS 16.0, tvOS 17.0, *)
public struct SettingsMenu: View {
@ObservedObject private var player: Player
private struct PlaybackSpeedMenuContent: View {
let speeds: Set<Float>
@ObservedObject var player: Player

public var body: some View {
// TODO: Remove when Xcode 15 has been released
#if os(iOS) || compiler(>=5.9)
var body: some View {
Picker("", selection: player.playbackSpeed) {
ForEach(playbackSpeeds, id: \.self) { speed in
Text("\(speed, specifier: "%g×")").tag(speed)
}
}
.pickerStyle(.inline)
}

private var playbackSpeeds: [Float] {
speeds.filter { speed in
player.playbackSpeedRange.contains(speed)
}
.sorted()
}
}

@available(iOS 16.0, tvOS 17.0, *)
private struct MediaSelectionMenuContent: View {
let characteristic: AVMediaCharacteristic
@ObservedObject var player: Player

var body: some View {
Picker("", selection: player.mediaOption(for: characteristic)) {
ForEach(mediaOptions, id: \.self) { option in
Text(option.displayName).tag(option)
}
}
.pickerStyle(.inline)
}

private var mediaOptions: [MediaSelectionOption] {
player.mediaSelectionOptions(for: characteristic)
}
}

@available(iOS 16.0, tvOS 17.0, *)
private struct SettingsMenuContent: View {
@ObservedObject var player: Player

var body: some View {
playbackSpeedMenu()
audibleMediaSelectionMenu()
legibleMediaSelectionMenu()
}

@ViewBuilder
private func playbackSpeedMenu() -> some View {
Menu {
PlaybackSpeedMenu(speeds: [0.5, 1, 1.25, 1.5, 2], player: player)
MediaSelectionMenu(characteristic: .audible, player: player)
MediaSelectionMenu(characteristic: .legible, player: player)
player.playbackSpeedMenu()
} label: {
Image(systemName: "ellipsis.circle")
.resizable()
.tint(.white)
.aspectRatio(contentMode: .fit)
Label("Playback Speed", systemImage: "speedometer")
}
.menuOrder(.fixed)
#endif
}

/// Creates a settings menu.
///
/// - Parameter player: The player which settings must be displayed for.
public init(player: Player) {
self.player = player
@ViewBuilder
private func audibleMediaSelectionMenu() -> some View {
Menu {
player.mediaSelectionMenu(characteristic: .audible)
} label: {
Label("Languages", systemImage: "waveform.circle")
}
}

@ViewBuilder
private func legibleMediaSelectionMenu() -> some View {
Menu {
player.mediaSelectionMenu(characteristic: .legible)
} label: {
Label("Subtitles", systemImage: "captions.bubble")
}
}
}

@available(iOS 16.0, tvOS 17.0, *)
struct SettingsMenu_Previews: PreviewProvider {
static var previews: some View {
SettingsMenu(player: Player())
.background(.black)
public extension Player {
/// Returns content for a standard player settings menu.
///
/// The returned view is meant to be used as content of a `Menu`. Using it for any other purpose has undefined
/// behavior.
func standardSettingMenu() -> some View {
SettingsMenuContent(player: self)
}

/// Returns content for a playback speed menu.
///
/// - Parameter speeds: The offered speeds.
///
/// The returned view is meant to be used as content of a `Menu`. Using it for any other purpose has undefined
/// behavior.
func playbackSpeedMenu(speeds: Set<Float> = [0.5, 1, 1.25, 1.5, 2]) -> some View {
PlaybackSpeedMenuContent(speeds: speeds, player: self)
}

/// Returns content for a media selection menu.
///
/// - Parameter characteristic: The characteristic for which selection is made.
///
/// The returned view is meant to be used as content of a `Menu`. Using it for any other purpose has undefined
/// behavior.
func mediaSelectionMenu(characteristic: AVMediaCharacteristic) -> some View {
MediaSelectionMenuContent(characteristic: characteristic, player: self)
}
}

0 comments on commit de8ba99

Please sign in to comment.