import SwiftUI /// Das Aufmacher-Bild oben auf der Startseite. /// /// Die Aktionsknöpfe kommen von außen, weil ein Film „Abspielen" und /// „Herunterladen" braucht, eine angefangene Folge dagegen „Weiterschauen". /// Der Baustein legt nur Bild, Verlauf und Textsatz fest. public struct HeroBanner: View { private let title: String private let metadata: String? private let summary: String? private let artworkURL: URL? private let aspectRatio: CGFloat private let actions: Actions public init( title: String, metadata: String? = nil, summary: String? = nil, artworkURL: URL?, // 16:9, weil Plex genau das als `art` liefert. Vorher stand hier // 3:4 — mit `scaledToFill` wurde das Bild dadurch links und rechts // kräftig beschnitten, und vom Aufmacher war nur ein Ausschnitt zu // sehen. aspectRatio: CGFloat = FlixyAspectRatio.hero, @ViewBuilder actions: () -> Actions ) { self.title = title self.metadata = metadata self.summary = summary self.artworkURL = artworkURL self.aspectRatio = aspectRatio self.actions = actions() } public var body: some View { FlixyArtwork(url: artworkURL, aspectRatio: aspectRatio) .overlay(alignment: .bottom) { // Der Verlauf endet in der Hintergrundfarbe der Seite, nicht in // Schwarz: sonst entsteht genau dort, wo der Aufmacher in die // erste Reihe übergeht, eine sichtbare Kante. LinearGradient( stops: [ .init(color: .clear, location: 0), .init(color: Color.flixyCanvas.opacity(0.75), location: 0.45), .init(color: Color.flixyCanvas, location: 1), ], startPoint: .top, endPoint: .bottom ) .frame(maxHeight: .infinity, alignment: .bottom) .allowsHitTesting(false) } .overlay(alignment: .bottomLeading) { VStack(alignment: .leading, spacing: FlixySpacing.snug) { Text(title) .font(.flixyHero) .foregroundStyle(Color.flixyTextPrimary) .lineLimit(2) if let metadata { Text(metadata) .font(.flixyCaption) .foregroundStyle(Color.flixyTextSecondary) } if let summary { Text(summary) .font(.flixyCaption) .foregroundStyle(Color.flixyTextSecondary) .lineLimit(2) } HStack(spacing: FlixySpacing.regular) { actions } .padding(.top, FlixySpacing.tight) } .padding(.horizontal, FlixySpacing.screenMargin) .padding(.bottom, FlixySpacing.loose) } } }