Files
yattee/Shared/Videos/VerticalCells.swift
Arkadiusz Fal a37f3e4a07 Adjust tvOS video cell dimensions for better layout
Reduced video cell and grid item sizes on tvOS to improve layout spacing
and visual consistency. Changed grid item size from 600 to 560 pixels,
and adjusted video cell frame dimensions accordingly.
2025-11-19 18:54:51 +01:00

116 lines
3.4 KiB
Swift

import Defaults
import SwiftUI
struct VerticalCells<Header: View>: View {
#if os(iOS)
@Environment(\.verticalSizeClass) private var verticalSizeClass
#endif
@Environment(\.loadMoreContentHandler) private var loadMoreContentHandler
@Environment(\.listingStyle) private var listingStyle
@Environment(\.navigationStyle) private var navigationStyle
var items = [ContentItem]()
var allowEmpty = false
var edgesIgnoringSafeArea = Edge.Set.horizontal
let header: Header?
@State private var gridSize = CGSize.zero
init(
items: [ContentItem],
allowEmpty: Bool = false,
edgesIgnoringSafeArea: Edge.Set = .horizontal,
@ViewBuilder header: @escaping () -> Header? = { nil }
) {
self.items = items
self.allowEmpty = allowEmpty
self.edgesIgnoringSafeArea = edgesIgnoringSafeArea
self.header = header()
}
init(
items: [ContentItem],
allowEmpty: Bool = false
) where Header == EmptyView {
self.init(items: items, allowEmpty: allowEmpty) { EmptyView() }
}
var body: some View {
ScrollView(.vertical, showsIndicators: scrollViewShowsIndicators) {
LazyVGrid(columns: adaptiveItem, alignment: .center) {
Section(header: header) {
ForEach(contentItems) { item in
ContentItemView(item: item)
.onAppear { loadMoreContentItemsIfNeeded(current: item) }
}
}
}
.padding()
}
.animation(nil)
#if os(iOS)
.edgesIgnoringSafeArea(navigationStyle == .sidebar ? [] : edgesIgnoringSafeArea)
#endif
#if os(macOS)
.background(Color.secondaryBackground)
.frame(minWidth: Constants.contentViewMinWidth)
#endif
}
var contentItems: [ContentItem] {
// Avoid sorting on every redraw - items should already be sorted from the source
// If sorting is truly needed, it should be done once in the model, not in the view
items.isEmpty ? (allowEmpty ? items : ContentItem.placeholders) : items
}
func loadMoreContentItemsIfNeeded(current item: ContentItem) {
let thresholdIndex = items.index(items.endIndex, offsetBy: -5)
if items.firstIndex(where: { $0.id == item.id }) == thresholdIndex {
loadMoreContentHandler()
}
}
var adaptiveItem: [GridItem] {
if listingStyle == .list {
return [.init(.flexible())]
}
return [GridItem(.adaptive(minimum: adaptiveGridItemMinimumSize, maximum: adaptiveGridItemMaximumSize))]
}
var adaptiveGridItemMinimumSize: Double {
#if os(iOS)
return verticalSizeClass == .regular ? 320 : 800
#elseif os(tvOS)
return 560
#else
return 320
#endif
}
var adaptiveGridItemMaximumSize: Double {
#if os(tvOS)
return 560
#else
return .infinity
#endif
}
var scrollViewShowsIndicators: Bool {
#if !os(tvOS)
true
#else
false
#endif
}
}
struct VeticalCells_Previews: PreviewProvider {
static var previews: some View {
VerticalCells(items: ContentItem.array(of: Array(repeating: Video.fixture, count: 30)))
.injectFixtureEnvironmentObjects()
}
}