feat: for tvOS
This commit is contained in:
@@ -48,9 +48,11 @@ struct BrowseView: View {
|
||||
.navigationDestination(for: ContentItem.self) { item in
|
||||
DetailView(item: item)
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.refreshable {
|
||||
await viewModel.loadContent()
|
||||
}
|
||||
#endif
|
||||
.task {
|
||||
viewModel.category = category
|
||||
await viewModel.loadContentIfNeeded()
|
||||
|
||||
@@ -3,9 +3,17 @@ import SwiftUI
|
||||
struct ContentCardView: View {
|
||||
let item: ContentItem
|
||||
|
||||
#if os(tvOS)
|
||||
private let cardWidth: CGFloat = 250
|
||||
#else
|
||||
private let cardWidth: CGFloat = 140
|
||||
#endif
|
||||
private let aspectRatio: CGFloat = 2.0 / 3.0
|
||||
|
||||
#if os(tvOS)
|
||||
@Environment(\.isFocused) private var isFocused
|
||||
#endif
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
// 海报
|
||||
@@ -54,6 +62,11 @@ struct ContentCardView: View {
|
||||
}
|
||||
}
|
||||
.frame(width: cardWidth)
|
||||
#if os(tvOS)
|
||||
.scaleEffect(isFocused ? 1.1 : 1.0)
|
||||
.shadow(color: isFocused ? .blue.opacity(0.4) : .clear, radius: 10)
|
||||
.animation(.easeInOut(duration: 0.2), value: isFocused)
|
||||
#endif
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
@@ -4,9 +4,15 @@ struct ContentGridView: View {
|
||||
let items: [ContentItem]
|
||||
var onNearEnd: (() -> Void)?
|
||||
|
||||
#if os(tvOS)
|
||||
private let columns = [
|
||||
GridItem(.adaptive(minimum: 250, maximum: 320), spacing: 24)
|
||||
]
|
||||
#else
|
||||
private let columns = [
|
||||
GridItem(.adaptive(minimum: 130, maximum: 180), spacing: 12)
|
||||
]
|
||||
#endif
|
||||
|
||||
var body: some View {
|
||||
LazyVGrid(columns: columns, spacing: 16) {
|
||||
|
||||
@@ -7,15 +7,47 @@ struct FilterBarView: View {
|
||||
var body: some View {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 16) {
|
||||
#if os(tvOS)
|
||||
filterPicker("排序", options: FilterState.defaultSorts, selection: $filter.sort)
|
||||
filterPicker("类型", options: FilterState.defaultGenres, selection: $filter.genre)
|
||||
filterPicker("地区", options: FilterState.defaultRegions, selection: $filter.region)
|
||||
filterPicker("年份", options: FilterState.defaultYears, selection: $filter.year)
|
||||
#else
|
||||
filterMenu("排序", options: FilterState.defaultSorts, selection: $filter.sort)
|
||||
filterMenu("类型", options: FilterState.defaultGenres, selection: $filter.genre)
|
||||
filterMenu("地区", options: FilterState.defaultRegions, selection: $filter.region)
|
||||
filterMenu("年份", options: FilterState.defaultYears, selection: $filter.year)
|
||||
#endif
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
private func filterPicker(_ title: String, options: [FilterOption], selection: Binding<String>) -> some View {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(options) { option in
|
||||
Button {
|
||||
selection.wrappedValue = option.value
|
||||
onFilterChanged()
|
||||
} label: {
|
||||
Text(option.name)
|
||||
.font(.subheadline)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(
|
||||
selection.wrappedValue == option.value
|
||||
? Color.accentColor
|
||||
: Color.secondary.opacity(0.12),
|
||||
in: Capsule()
|
||||
)
|
||||
.foregroundStyle(selection.wrappedValue == option.value ? .white : .primary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
private func filterMenu(_ title: String, options: [FilterOption], selection: Binding<String>) -> some View {
|
||||
Menu {
|
||||
ForEach(options) { option in
|
||||
@@ -52,4 +84,5 @@ struct FilterBarView: View {
|
||||
guard !value.isEmpty else { return nil }
|
||||
return options.first { $0.value == value }?.name
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user