Fit player artwork to the available screen height

This commit is contained in:
2026-09-13 10:56:43 +03:00
parent cbd3c0f6f4
commit aa0b0f0d8e
+8 -3
View File
@@ -6,6 +6,7 @@ struct ContentView: View {
var body: some View { var body: some View {
NavigationStack { NavigationStack {
GeometryReader { viewport in
ScrollView { ScrollView {
VStack(spacing: 20) { VStack(spacing: 20) {
HStack(spacing: 8) { HStack(spacing: 8) {
@@ -16,7 +17,7 @@ struct ContentView: View {
if store.connection == nil { if store.connection == nil {
PairingView(store: store, discovery: store.discovery) PairingView(store: store, discovery: store.discovery)
} else { } else {
PlayerView(store: store) PlayerView(store: store, coverSize: min(280, max(120, min(viewport.size.width - 40, viewport.size.height * 0.30))))
if !store.connected && !store.needsPairing { if !store.connected && !store.needsPairing {
Button("Подключиться снова") { store.resume() } Button("Подключиться снова") { store.resume() }
@@ -33,6 +34,7 @@ struct ContentView: View {
}.padding(20) }.padding(20)
} }
.scrollDismissesKeyboard(.interactively) .scrollDismissesKeyboard(.interactively)
}
.background(Color(.systemGroupedBackground)) .background(Color(.systemGroupedBackground))
.navigationTitle("MusicBridge") .navigationTitle("MusicBridge")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
@@ -188,6 +190,7 @@ private struct PairingView: View {
private struct PlayerView: View { private struct PlayerView: View {
@ObservedObject var store: PlayerStore @ObservedObject var store: PlayerStore
let coverSize: CGFloat
@Environment(\.dynamicTypeSize) private var textSize @Environment(\.dynamicTypeSize) private var textSize
@State private var seekValue = 0.0 @State private var seekValue = 0.0
@State private var seeking = false @State private var seeking = false
@@ -204,7 +207,9 @@ private struct PlayerView: View {
.fill(LinearGradient(colors: [.indigo.opacity(0.85), .teal.opacity(0.75)], startPoint: .topLeading, endPoint: .bottomTrailing)) .fill(LinearGradient(colors: [.indigo.opacity(0.85), .teal.opacity(0.75)], startPoint: .topLeading, endPoint: .bottomTrailing))
.overlay { Image(systemName: "waveform.path").font(.system(size: 72, weight: .light)).foregroundStyle(.white.opacity(0.9)) } .overlay { Image(systemName: "waveform.path").font(.system(size: 72, weight: .light)).foregroundStyle(.white.opacity(0.9)) }
} }
}.frame(maxWidth: textSize.isAccessibilitySize ? 210 : 300).aspectRatio(1, contentMode: .fit).clipShape(RoundedRectangle(cornerRadius: 28)) }.frame(width: textSize.isAccessibilitySize ? min(180, coverSize) : coverSize,
height: textSize.isAccessibilitySize ? min(180, coverSize) : coverSize)
.clipShape(RoundedRectangle(cornerRadius: 28))
.accessibilityLabel("Обложка трека") .accessibilityLabel("Обложка трека")
if let error = store.artworkError { if let error = store.artworkError {
Text(error).font(.footnote).foregroundStyle(.secondary) Text(error).font(.footnote).foregroundStyle(.secondary)
@@ -213,7 +218,7 @@ private struct PlayerView: View {
if let state = store.state { if let state = store.state {
Text(state.hasSession ? (state.title.isEmpty ? "Без названия" : state.title) : "Откройте плеер на ПК") Text(state.hasSession ? (state.title.isEmpty ? "Без названия" : state.title) : "Откройте плеер на ПК")
.font(.title2.bold()).multilineTextAlignment(.center) .font(.title2.bold()).multilineTextAlignment(.center)
Text(state.artist).foregroundStyle(.secondary) Text(state.artist).foregroundStyle(.secondary).multilineTextAlignment(.center)
TimelineView(.periodic(from: .now, by: 0.25)) { timeline in TimelineView(.periodic(from: .now, by: 0.25)) { timeline in
let position = state.position(elapsed: timeline.date.timeIntervalSince(store.receivedAt)) let position = state.position(elapsed: timeline.date.timeIntervalSince(store.receivedAt))
VStack { VStack {