From e53f74d0fadbb6c2fd5c814420c896acd36b6b48 Mon Sep 17 00:00:00 2001 From: Renato Ferraz Date: Mon, 22 Dec 2025 12:46:26 -0300 Subject: [PATCH 1/4] Refactors chapter list and improves UI Rebuilds the chapter list screen with a more modern and visually appealing design. Improves user experience by highlighting the currently playing chapter and providing a progress bar. Enhances aesthetics with subtle animations, background gradients, and a capsule indicator for a cleaner look. --- .../Podcast/Views/Player/ChaptersView.swift | 235 +++++++++++++++--- .../Podcast/Views/Player/FullPlayerView.swift | 33 ++- .../Sources/Podcast/Views/PodcastView.swift | 2 +- 3 files changed, 229 insertions(+), 41 deletions(-) diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift index aa7fad46..fa8ea469 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift @@ -8,8 +8,8 @@ struct ChaptersView: View { @Bindable private var playerManager: PodcastPlayerManager @Binding private var isShowingChapterDialog: Bool - @State var backgroundGradientColors: [Color] = [.black, .black] - @State var isDarkBackground: Bool = false + @State private var backgroundGradientColors: [Color] = [.black, .black] + @State private var isDarkBackground = false private let backgroundGradientStyle: PodcastBackgroundGradientStyle @@ -30,13 +30,23 @@ struct ChaptersView: View { backgroundGradientColors: backgroundGradientColors, usesGradient: false ) - chapters + .ignoresSafeArea() + + content } .trackScreen( AnalyticsConstants.Screen.podcastChapters.name, previous: nil, analytics: analytics ) + .safeAreaInset(edge: .top) { + Capsule() + .fill(.white.opacity(isDarkBackground ? 0.35 : 0.45)) + .frame(width: 44, height: 5) + .padding(.top, 8) + .padding(.bottom, 6) + .accessibilityHidden(true) + } .preferredColorScheme(isDarkBackground ? .dark : .light) .onAppear { updateBackgroundGradient(data: playerManager.currentChapter?.artworkData) @@ -47,50 +57,199 @@ struct ChaptersView: View { } } +// MARK: - UI + private extension ChaptersView { - var chapters: some View { - List(playerManager.chapters, id: \.id) { chapter in - Button(action: { - playerManager.seek(to: chapter.start.seconds) - isShowingChapterDialog.toggle() - }, label: { - HStack(spacing: 20) { - PodcastImageView( - artworkData: chapter.artworkData, - location: .chapter, - fallback: { Rectangle().fill(.clear) }) - .frame(width: 45, height: 45) - .cornerRadius(8) - - VStack(alignment: .leading, spacing: 10) { - Text(chapter.title).bold().font(.body) - - HStack(spacing: 10) { - HStack(spacing: 4) { - Image(systemName: "clock").font(.system(size: 14)) - Text(chapter.startString) - } - HStack(spacing: 4) { - Image(systemName: "microphone").font(.system(size: 14)) - Text(chapter.durationString) - } - Spacer() + var content: some View { + ScrollView { + LazyVStack(spacing: 10) { + ForEach(playerManager.chapters, id: \.id) { chapter in + chapterRow(chapter) + } + } + .padding(.horizontal, 16) + .padding(.top, 12) + .padding(.bottom, 24) + } + .scrollIndicators(.hidden) + } + + func chapterRow(_ chapter: PodcastChapter) -> some View { + let active = isActive(chapter) + + return Button { + playerManager.seek(to: chapter.start.seconds) + isShowingChapterDialog.toggle() + } label: { + HStack(spacing: 12) { + PodcastImageView( + artworkData: chapter.artworkData, + location: .chapter, + fallback: { Rectangle().fill(.clear) } + ) + .frame(width: 44, height: 44) + .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) + + VStack(alignment: .leading, spacing: 8) { + Text(chapter.title) + .font(active ? .body.weight(.bold) : .body.weight(.semibold)) + .foregroundStyle(.primary) + .multilineTextAlignment(.leading) + + HStack(spacing: 12) { + HStack(spacing: 6) { + Image(systemName: "clock") + .font(.system(size: 14)) + Text(chapter.startString) + } + + Spacer() + + HStack(spacing: 6) { + Text(chapter.durationString) + Image(systemName: "microphone") + .font(.system(size: 14)) + } + } + .font(.callout) + .foregroundStyle(.secondary) + + if active { + TimelineView(.animation) { _ in + progressBar(for: chapter) } - .font(.callout) + .transition(.opacity) } } - }) - .listRowBackground(chapter.backgroundColor(at: playerManager.currentTime, using: backgroundGradientColors)) - .accessibilityLabel("\(chapter.title), começando em \(chapter.startString) com duração de \(chapter.durationString).") + + Spacer(minLength: 0) + } + .padding(.vertical, 12) + .padding(.horizontal, 12) + .contentShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + } + .buttonStyle(.plain) + .accessibilityLabel("\(chapter.title), começando em \(chapter.startString) com duração de \(chapter.durationString).") + .background { + cardBackground(for: chapter) } - .scrollContentBackground(.hidden) + .scaleEffect(active ? 1.015 : 1.0) + .animation(.spring(response: 0.35, dampingFraction: 0.85), value: active) } } +// MARK: - Card background (glass + highlight + “band” que acompanha o tempo) + private extension ChaptersView { - func updateBackgroundGradient( - data: Data? = nil - ) { + func cardBackground(for chapter: PodcastChapter) -> some View { + let active = isActive(chapter) + + return RoundedRectangle(cornerRadius: 16, style: .continuous) + .fill(.ultraThinMaterial) + .overlay { + // Tint base (fica mais forte quando ativo) + RoundedRectangle(cornerRadius: 16, style: .continuous) + .fill(chapterTintColor(for: chapter)) + .opacity(active ? 0.38 : 0.18) + } + .overlay { + // Highlight que “acompanha o tempo”: uma faixa luminosa posicionada pelo progress + GeometryReader { proxy in + TimelineView(.animation) { _ in + let progress = progress(for: chapter) + let width = proxy.size.width + let bandWidth = min(140, width * 0.38) + let xOffset = max(0, min(1, progress)) * max(0, width - bandWidth) + + Rectangle() + .fill( + LinearGradient( + colors: [ + .clear, + .white.opacity(isDarkBackground ? 0.32 : 0.25), + .clear + ], + startPoint: .top, + endPoint: .bottom + ) + ) + .frame(width: bandWidth) + .offset(x: xOffset) + .blur(radius: 10) + .opacity(active ? 1.0 : 0.0) + .animation(.linear(duration: 0.12), value: xOffset) + } + } + .mask( + RoundedRectangle(cornerRadius: 16, style: .continuous) + ) + .allowsHitTesting(false) + } + .overlay { + RoundedRectangle(cornerRadius: 16, style: .continuous) + .strokeBorder( + .white.opacity( + active ? 0.35 : (isDarkBackground ? 0.10 : 0.18) + ), + lineWidth: active ? 1.2 : 0.5 + ) + } + } + + func chapterTintColor(for chapter: PodcastChapter) -> Color { + chapter.backgroundColor(at: playerManager.currentTime, using: backgroundGradientColors) + } +} + +// MARK: - Progress bar + +private extension ChaptersView { + func progressBar(for chapter: PodcastChapter) -> some View { + let progress = progress(for: chapter) + + return GeometryReader { proxy in + let proxyW = proxy.size.width + let fillW = max(0, min(1, progress)) * proxyW + + ZStack(alignment: .leading) { + Capsule(style: .continuous) + .fill(.white.opacity(isDarkBackground ? 0.12 : 0.16)) + + Capsule(style: .continuous) + .fill(.white.opacity(isDarkBackground ? 0.75 : 0.65)) + .frame(width: max(2, fillW)) + .animation(.linear(duration: 0.12), value: fillW) + } + } + .frame(height: 3) + .padding(.top, 2) + .accessibilityHidden(true) + } +} + +// MARK: - Active/progress helpers + +private extension ChaptersView { + func isActive(_ chapter: PodcastChapter) -> Bool { + let time = playerManager.currentTime + let start = chapter.start.seconds + let end = chapter.end.seconds + return time >= start && time < end + } + + func progress(for chapter: PodcastChapter) -> CGFloat { + let time = playerManager.currentTime + let start = chapter.start.seconds + let end = chapter.end.seconds + let denom = max(0.001, end - start) + return CGFloat((time - start) / denom) + } +} + +// MARK: - Background + +private extension ChaptersView { + func updateBackgroundGradient(data: Data? = nil) { BackgroundViewModel.backgroundGradient( data: data, artworkURL: Constants.coverURL, diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift index df8d1ed0..d7b1b698 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift @@ -69,6 +69,9 @@ struct FullPlayerView: View { var body: some View { player + .overlay(alignment: .top) { + capsuleTop + } .trackScreen(AnalyticsConstants.Screen.podcastFullPlayer.name, analytics: analytics) .sheet(isPresented: $isShowingChapterDialog) { ChaptersView( @@ -76,7 +79,6 @@ struct FullPlayerView: View { backgroundGradientStyle: backgroundGradientStyle, isShowingChapterDialog: $isShowingChapterDialog ) - .presentationDragIndicator(.visible) } } @@ -146,7 +148,7 @@ struct FullPlayerView: View { Spacer() actions } - .padding(.top, 20) + .padding(.top, 5) artworkView podcastTitle(podcast.title) @@ -154,7 +156,9 @@ struct FullPlayerView: View { Spacer() progressSlider + .padding(.bottom, 20) playbackControls + .padding(.bottom, 20) volumeSlider } .padding(.horizontal) @@ -416,6 +420,8 @@ private extension FullPlayerView { )) }, label: { Image(systemName: "music.note.list") + .opacity(0.6) + .font(.system(size: 15)) }) .font(.system(size: 24)) .accessibilityLabel("Lista de capítulos") @@ -632,11 +638,32 @@ private extension FullPlayerView { } } +private extension FullPlayerView { + var windowSafeTop: CGFloat { + (UIApplication.shared.connectedScenes.first as? UIWindowScene)? + .keyWindow? + .safeAreaInsets.top ?? 0 + } + + var capsuleTop: some View { + Capsule() + .fill(.white.opacity(0.55)) + .frame(width: 44, height: 5) + .padding(.top, verticalSizeClass == .compact ? 30 : 8) + .padding(.bottom, 10) + .allowsHitTesting(false) + .accessibilityHidden(true) + .zIndex(999) + } +} + // MARK: - Preview - #Preview { @Previewable @State var playerManager = PodcastPlayerManager() + let analytics = AnalyticsManager() + let mockPodcast = PodcastDB( postId: "1", title: "MacMagazine no Ar #123: Especial WWDC 2024", @@ -660,4 +687,6 @@ private extension FullPlayerView { playerManager.duration = 2_730 playerManager.currentTime = 450 } + .environmentObject(analytics) + } diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift index ca430927..4ecf08e6 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift @@ -65,7 +65,7 @@ public struct PodcastView: View { playerManager: podcastPlayerManager, backgroundGradientStyle: .fourTone ) - .presentationDragIndicator(.visible) + .presentationDragIndicator(.hidden) } } } From 90da9fe8df79d2f9f887ce59d99fe9838edea491 Mon Sep 17 00:00:00 2001 From: Renato Ferraz Date: Mon, 22 Dec 2025 13:54:55 -0300 Subject: [PATCH 2/4] Improves chapter highlighting and UI Enhances the visual feedback for the currently playing chapter in the podcast player. This commit refactors the chapter highlighting logic to use a more reliable method for determining the active chapter. It also improves the progress bar for chapters by switching to a Slider presentation style to match the current progress. The capsule on top of the full player is removed. The presentation drag indicator is enabled on the PodcastView --- .../Podcast/Views/Player/ChaptersView.swift | 52 +++++++------------ .../Podcast/Views/Player/FullPlayerView.swift | 22 -------- .../Sources/Podcast/Views/PodcastView.swift | 2 +- 3 files changed, 20 insertions(+), 56 deletions(-) diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift index fa8ea469..3f66a24a 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift @@ -75,8 +75,6 @@ private extension ChaptersView { } func chapterRow(_ chapter: PodcastChapter) -> some View { - let active = isActive(chapter) - return Button { playerManager.seek(to: chapter.start.seconds) isShowingChapterDialog.toggle() @@ -92,7 +90,7 @@ private extension ChaptersView { VStack(alignment: .leading, spacing: 8) { Text(chapter.title) - .font(active ? .body.weight(.bold) : .body.weight(.semibold)) + .font(isActive(for: chapter) ? .body.weight(.bold) : .body.weight(.semibold)) .foregroundStyle(.primary) .multilineTextAlignment(.leading) @@ -114,7 +112,7 @@ private extension ChaptersView { .font(.callout) .foregroundStyle(.secondary) - if active { + if isActive(for: chapter) { TimelineView(.animation) { _ in progressBar(for: chapter) } @@ -133,8 +131,8 @@ private extension ChaptersView { .background { cardBackground(for: chapter) } - .scaleEffect(active ? 1.015 : 1.0) - .animation(.spring(response: 0.35, dampingFraction: 0.85), value: active) + .scaleEffect(isActive(for: chapter) ? 1.015 : 1.0) + .animation(.spring(response: 0.35, dampingFraction: 0.85), value: isActive(for: chapter)) } } @@ -142,15 +140,12 @@ private extension ChaptersView { private extension ChaptersView { func cardBackground(for chapter: PodcastChapter) -> some View { - let active = isActive(chapter) - return RoundedRectangle(cornerRadius: 16, style: .continuous) .fill(.ultraThinMaterial) .overlay { - // Tint base (fica mais forte quando ativo) RoundedRectangle(cornerRadius: 16, style: .continuous) .fill(chapterTintColor(for: chapter)) - .opacity(active ? 0.38 : 0.18) + .opacity(playerManager.currentChapter == chapter ? 0.38 : 0.18) } .overlay { // Highlight que “acompanha o tempo”: uma faixa luminosa posicionada pelo progress @@ -176,7 +171,7 @@ private extension ChaptersView { .frame(width: bandWidth) .offset(x: xOffset) .blur(radius: 10) - .opacity(active ? 1.0 : 0.0) + .opacity(isActive(for: chapter) ? 1.0 : 0.0) .animation(.linear(duration: 0.12), value: xOffset) } } @@ -189,9 +184,9 @@ private extension ChaptersView { RoundedRectangle(cornerRadius: 16, style: .continuous) .strokeBorder( .white.opacity( - active ? 0.35 : (isDarkBackground ? 0.10 : 0.18) + isActive(for: chapter) ? 0.35 : (isDarkBackground ? 0.10 : 0.18) ), - lineWidth: active ? 1.2 : 0.5 + lineWidth: isActive(for: chapter) ? 1.2 : 0.5 ) } } @@ -205,36 +200,27 @@ private extension ChaptersView { private extension ChaptersView { func progressBar(for chapter: PodcastChapter) -> some View { - let progress = progress(for: chapter) - - return GeometryReader { proxy in - let proxyW = proxy.size.width - let fillW = max(0, min(1, progress)) * proxyW + let progress = Double(progress(for: chapter)) - ZStack(alignment: .leading) { - Capsule(style: .continuous) - .fill(.white.opacity(isDarkBackground ? 0.12 : 0.16)) - - Capsule(style: .continuous) - .fill(.white.opacity(isDarkBackground ? 0.75 : 0.65)) - .frame(width: max(2, fillW)) - .animation(.linear(duration: 0.12), value: fillW) - } - } + return Slider( + value: .constant(progress), + in: 0...1 + ) + .tint(.white.opacity(isDarkBackground ? 0.75 : 0.65)) + .sliderThumbVisibility(.hidden) .frame(height: 3) .padding(.top, 2) .accessibilityHidden(true) + .allowsHitTesting(false) } } // MARK: - Active/progress helpers private extension ChaptersView { - func isActive(_ chapter: PodcastChapter) -> Bool { - let time = playerManager.currentTime - let start = chapter.start.seconds - let end = chapter.end.seconds - return time >= start && time < end + @MainActor + func isActive(for chapter: PodcastChapter) -> Bool { + playerManager.currentChapter == chapter } func progress(for chapter: PodcastChapter) -> CGFloat { diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift index d7b1b698..156835a9 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift @@ -69,9 +69,6 @@ struct FullPlayerView: View { var body: some View { player - .overlay(alignment: .top) { - capsuleTop - } .trackScreen(AnalyticsConstants.Screen.podcastFullPlayer.name, analytics: analytics) .sheet(isPresented: $isShowingChapterDialog) { ChaptersView( @@ -638,25 +635,6 @@ private extension FullPlayerView { } } -private extension FullPlayerView { - var windowSafeTop: CGFloat { - (UIApplication.shared.connectedScenes.first as? UIWindowScene)? - .keyWindow? - .safeAreaInsets.top ?? 0 - } - - var capsuleTop: some View { - Capsule() - .fill(.white.opacity(0.55)) - .frame(width: 44, height: 5) - .padding(.top, verticalSizeClass == .compact ? 30 : 8) - .padding(.bottom, 10) - .allowsHitTesting(false) - .accessibilityHidden(true) - .zIndex(999) - } -} - // MARK: - Preview - #Preview { diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift index 4ecf08e6..ca430927 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/PodcastView.swift @@ -65,7 +65,7 @@ public struct PodcastView: View { playerManager: podcastPlayerManager, backgroundGradientStyle: .fourTone ) - .presentationDragIndicator(.hidden) + .presentationDragIndicator(.visible) } } } From 13d200e5a2f4f0f3f20b1caf056c844abd26fac7 Mon Sep 17 00:00:00 2001 From: Renato Ferraz Date: Mon, 22 Dec 2025 14:31:22 -0300 Subject: [PATCH 3/4] Removes highlighting effect from chapter view Removes a highlighting effect from the chapter view in the player, which was implemented as a moving luminous band that followed the progress. The visual effect is no longer deemed necessary. --- .../Podcast/Views/Player/ChaptersView.swift | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift index 3f66a24a..e408d0b2 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift @@ -142,44 +142,6 @@ private extension ChaptersView { func cardBackground(for chapter: PodcastChapter) -> some View { return RoundedRectangle(cornerRadius: 16, style: .continuous) .fill(.ultraThinMaterial) - .overlay { - RoundedRectangle(cornerRadius: 16, style: .continuous) - .fill(chapterTintColor(for: chapter)) - .opacity(playerManager.currentChapter == chapter ? 0.38 : 0.18) - } - .overlay { - // Highlight que “acompanha o tempo”: uma faixa luminosa posicionada pelo progress - GeometryReader { proxy in - TimelineView(.animation) { _ in - let progress = progress(for: chapter) - let width = proxy.size.width - let bandWidth = min(140, width * 0.38) - let xOffset = max(0, min(1, progress)) * max(0, width - bandWidth) - - Rectangle() - .fill( - LinearGradient( - colors: [ - .clear, - .white.opacity(isDarkBackground ? 0.32 : 0.25), - .clear - ], - startPoint: .top, - endPoint: .bottom - ) - ) - .frame(width: bandWidth) - .offset(x: xOffset) - .blur(radius: 10) - .opacity(isActive(for: chapter) ? 1.0 : 0.0) - .animation(.linear(duration: 0.12), value: xOffset) - } - } - .mask( - RoundedRectangle(cornerRadius: 16, style: .continuous) - ) - .allowsHitTesting(false) - } .overlay { RoundedRectangle(cornerRadius: 16, style: .continuous) .strokeBorder( From 87fa574ceef164a5c3e52a6976c592bc7e218092 Mon Sep 17 00:00:00 2001 From: Renato Ferraz Date: Mon, 22 Dec 2025 14:44:26 -0300 Subject: [PATCH 4/4] Enhances chapter selection UI Improves the appearance of the chapter selection UI. This commit makes the following changes: - Adds a drag indicator for the full player view to improve discoverability. - Adjusts the scale effect of the chapter card when active for better visual feedback. - Removes the capsule indicator from the ChaptersView which is no longer needed as the drag indicator has been added to the full player. --- .../Sources/Podcast/Views/Player/ChaptersView.swift | 10 +--------- .../Sources/Podcast/Views/Player/FullPlayerView.swift | 1 + 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift index e408d0b2..8237da2b 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/ChaptersView.swift @@ -39,14 +39,6 @@ struct ChaptersView: View { previous: nil, analytics: analytics ) - .safeAreaInset(edge: .top) { - Capsule() - .fill(.white.opacity(isDarkBackground ? 0.35 : 0.45)) - .frame(width: 44, height: 5) - .padding(.top, 8) - .padding(.bottom, 6) - .accessibilityHidden(true) - } .preferredColorScheme(isDarkBackground ? .dark : .light) .onAppear { updateBackgroundGradient(data: playerManager.currentChapter?.artworkData) @@ -131,7 +123,7 @@ private extension ChaptersView { .background { cardBackground(for: chapter) } - .scaleEffect(isActive(for: chapter) ? 1.015 : 1.0) + .scaleEffect(isActive(for: chapter) ? 1.035 : 1.0) .animation(.spring(response: 0.35, dampingFraction: 0.85), value: isActive(for: chapter)) } } diff --git a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift index 156835a9..487a32f1 100644 --- a/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift +++ b/MacMagazine/Features/PodcastLibrary/Sources/Podcast/Views/Player/FullPlayerView.swift @@ -76,6 +76,7 @@ struct FullPlayerView: View { backgroundGradientStyle: backgroundGradientStyle, isShowingChapterDialog: $isShowingChapterDialog ) + .presentationDragIndicator(.visible) } }