mirror of
https://github.com/yattee/yattee.git
synced 2026-06-04 13:54:19 +00:00
Match tvOS seek preview to iOS glass design
This commit is contained in:
@@ -8,70 +8,76 @@
|
||||
#if os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
/// Glass capsule showing the current chapter title above the tvOS seek preview.
|
||||
struct TVChapterCapsuleView: View {
|
||||
let title: String
|
||||
|
||||
var body: some View {
|
||||
Text(title)
|
||||
.font(.system(size: 24, weight: .medium))
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 6)
|
||||
.glassBackground(
|
||||
.regular,
|
||||
in: .capsule,
|
||||
fallback: .ultraThinMaterial,
|
||||
colorScheme: .dark
|
||||
)
|
||||
.shadow(radius: 4)
|
||||
}
|
||||
}
|
||||
|
||||
/// Preview thumbnail displayed during scrubbing on tvOS.
|
||||
/// Scaled up for TV viewing distance.
|
||||
struct TVSeekPreviewView: View {
|
||||
let storyboard: Storyboard
|
||||
let seekTime: TimeInterval
|
||||
let chapters: [VideoChapter]
|
||||
|
||||
@State private var thumbnail: UIImage?
|
||||
@State private var loadTask: Task<Void, Never>?
|
||||
|
||||
/// The current chapter based on seek time.
|
||||
private var currentChapter: VideoChapter? {
|
||||
chapters.last { $0.startTime <= seekTime }
|
||||
}
|
||||
|
||||
private let thumbnailWidth: CGFloat = 320
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 12) {
|
||||
// Chapter name (only shown if chapters exist, larger for TV)
|
||||
// Constrained to thumbnail width to prevent expanding the preview
|
||||
if let chapter = currentChapter {
|
||||
Text(chapter.title)
|
||||
.font(.system(size: 28, weight: .medium))
|
||||
.lineLimit(2)
|
||||
.multilineTextAlignment(.center)
|
||||
.truncationMode(.tail)
|
||||
.foregroundStyle(.white)
|
||||
.shadow(color: .black.opacity(0.8), radius: 3, x: 0, y: 1)
|
||||
.frame(maxWidth: thumbnailWidth)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
|
||||
// Thumbnail with timestamp overlay (scaled up for TV)
|
||||
ZStack(alignment: .bottom) {
|
||||
Group {
|
||||
if let thumbnail {
|
||||
Image(uiImage: thumbnail)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
} else {
|
||||
// Placeholder while loading
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.3))
|
||||
}
|
||||
// Thumbnail with timestamp overlay (scaled up for TV)
|
||||
ZStack(alignment: .bottom) {
|
||||
Group {
|
||||
if let thumbnail {
|
||||
Image(uiImage: thumbnail)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
} else {
|
||||
// Placeholder while loading
|
||||
Rectangle()
|
||||
.fill(Color.gray.opacity(0.3))
|
||||
}
|
||||
|
||||
// Timestamp overlaid at bottom center (larger for TV)
|
||||
Text(seekTime.formattedAsTimestamp)
|
||||
.font(.system(size: 36, weight: .medium))
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(.black.opacity(0.7))
|
||||
.clipShape(.rect(cornerRadius: 6))
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
.frame(width: thumbnailWidth, height: 180)
|
||||
.clipShape(.rect(cornerRadius: 8))
|
||||
.clipped()
|
||||
|
||||
// Timestamp overlaid at bottom center (larger for TV)
|
||||
Text(seekTime.formattedAsTimestamp)
|
||||
.font(.system(size: 36, weight: .medium))
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(.black.opacity(0.7))
|
||||
.clipShape(.rect(cornerRadius: 6))
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
.padding(16)
|
||||
.background(.ultraThinMaterial)
|
||||
.clipShape(.rect(cornerRadius: 16))
|
||||
.clipShape(.rect(cornerRadius: 4))
|
||||
.padding(4)
|
||||
.glassBackground(
|
||||
.regular,
|
||||
in: .rect(cornerRadius: 8),
|
||||
fallback: .ultraThinMaterial,
|
||||
colorScheme: .dark
|
||||
)
|
||||
.shadow(radius: 4)
|
||||
.onChange(of: seekTime) { _, newTime in
|
||||
loadThumbnail(for: newTime)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user