mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
separate tvOS View
This commit is contained in:
parent
4ec6f35c5d
commit
13ef96cd02
@ -3,7 +3,8 @@ import Foundation
|
|||||||
import SDWebImageSwiftUI
|
import SDWebImageSwiftUI
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ChapterView: View {
|
#if !os(tvOS)
|
||||||
|
struct ChapterView: View {
|
||||||
var chapter: Chapter
|
var chapter: Chapter
|
||||||
var nextChapterStart: Double?
|
var nextChapterStart: Double?
|
||||||
|
|
||||||
@ -23,15 +24,12 @@ struct ChapterView: View {
|
|||||||
player.backend.seek(to: chapter.start, seekType: .userInteracted)
|
player.backend.seek(to: chapter.start, seekType: .userInteracted)
|
||||||
}) {
|
}) {
|
||||||
Group {
|
Group {
|
||||||
#if os(tvOS)
|
|
||||||
horizontalChapter
|
|
||||||
#else
|
|
||||||
verticalChapter
|
verticalChapter
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|
||||||
.onReceive(PlayerTimeModel.shared.$currentTime) { cmTime in
|
.onReceive(PlayerTimeModel.shared.$currentTime) { cmTime in
|
||||||
let time = CMTimeGetSeconds(cmTime)
|
let time = CMTimeGetSeconds(cmTime)
|
||||||
if time >= self.chapter.start, self.nextChapterStart == nil || time < self.nextChapterStart! {
|
if time >= self.chapter.start, self.nextChapterStart == nil || time < self.nextChapterStart! {
|
||||||
@ -43,25 +41,6 @@ struct ChapterView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(tvOS)
|
|
||||||
|
|
||||||
var horizontalChapter: some View {
|
|
||||||
HStack(spacing: 12) {
|
|
||||||
if !chapter.image.isNil {
|
|
||||||
smallImage(chapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
|
||||||
Text(chapter.title)
|
|
||||||
.font(.headline)
|
|
||||||
Text(chapter.start.formattedAsPlaybackTime(allowZero: true) ?? "")
|
|
||||||
.font(.system(.subheadline).monospacedDigit())
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
var verticalChapter: some View {
|
var verticalChapter: some View {
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
if !chapter.image.isNil {
|
if !chapter.image.isNil {
|
||||||
@ -80,7 +59,6 @@ struct ChapterView: View {
|
|||||||
.frame(maxWidth: !chapter.image.isNil ? Self.thumbnailWidth : nil, alignment: .leading)
|
.frame(maxWidth: !chapter.image.isNil ? Self.thumbnailWidth : nil, alignment: .leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
@ViewBuilder func smallImage(_ chapter: Chapter) -> some View {
|
@ViewBuilder func smallImage(_ chapter: Chapter) -> some View {
|
||||||
WebImage(url: chapter.image, options: [.lowPriority])
|
WebImage(url: chapter.image, options: [.lowPriority])
|
||||||
@ -90,11 +68,8 @@ struct ChapterView: View {
|
|||||||
}
|
}
|
||||||
.indicator(.activity)
|
.indicator(.activity)
|
||||||
.frame(width: Self.thumbnailWidth, height: Self.thumbnailHeight)
|
.frame(width: Self.thumbnailWidth, height: Self.thumbnailHeight)
|
||||||
#if os(tvOS)
|
|
||||||
.mask(RoundedRectangle(cornerRadius: 12))
|
|
||||||
#else
|
|
||||||
.mask(RoundedRectangle(cornerRadius: 6))
|
.mask(RoundedRectangle(cornerRadius: 6))
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static var thumbnailWidth: Double {
|
static var thumbnailWidth: Double {
|
||||||
@ -104,11 +79,71 @@ struct ChapterView: View {
|
|||||||
static var thumbnailHeight: Double {
|
static var thumbnailHeight: Double {
|
||||||
thumbnailWidth / 1.7777
|
thumbnailWidth / 1.7777
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
struct ChapterViewTVOS: View {
|
||||||
|
var chapter: Chapter
|
||||||
|
var player = PlayerModel.shared
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button {
|
||||||
|
player.backend.seek(to: chapter.start, seekType: .userInteracted)
|
||||||
|
} label: {
|
||||||
|
Group {
|
||||||
|
horizontalChapter
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
|
||||||
|
var horizontalChapter: some View {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
if !chapter.image.isNil {
|
||||||
|
smallImage(chapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text(chapter.title)
|
||||||
|
.font(.headline)
|
||||||
|
Text(chapter.start.formattedAsPlaybackTime(allowZero: true) ?? "")
|
||||||
|
.font(.system(.subheadline).monospacedDigit())
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder func smallImage(_ chapter: Chapter) -> some View {
|
||||||
|
WebImage(url: chapter.image, options: [.lowPriority])
|
||||||
|
.resizable()
|
||||||
|
.placeholder {
|
||||||
|
ProgressView()
|
||||||
|
}
|
||||||
|
.indicator(.activity)
|
||||||
|
.frame(width: Self.thumbnailWidth, height: Self.thumbnailHeight)
|
||||||
|
.mask(RoundedRectangle(cornerRadius: 12))
|
||||||
|
}
|
||||||
|
|
||||||
|
static var thumbnailWidth: Double {
|
||||||
|
250
|
||||||
|
}
|
||||||
|
|
||||||
|
static var thumbnailHeight: Double {
|
||||||
|
thumbnailWidth / 1.7777
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct ChapterView_Preview: PreviewProvider {
|
struct ChapterView_Preview: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ChapterView(chapter: .init(title: "Chapter", start: 30), chapterIndex: 0)
|
#if os(tvOS)
|
||||||
|
ChapterViewTVOS(chapter: .init(title: "Chapter", start: 30))
|
||||||
.injectFixtureEnvironmentObjects()
|
.injectFixtureEnvironmentObjects()
|
||||||
|
#else
|
||||||
|
ChapterView(chapter: .init(title: "Chapter", start: 30), nextChapterStart: nil, chapterIndex: 0)
|
||||||
|
.injectFixtureEnvironmentObjects()
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ struct ChaptersView: View {
|
|||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
ForEach(chapters) { chapter in
|
ForEach(chapters) { chapter in
|
||||||
ChapterView(chapter: chapter)
|
ChapterViewTVOS(chapter: chapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
@ -29,51 +29,53 @@ struct ChaptersView: View {
|
|||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
#else
|
#else
|
||||||
ScrollView(.horizontal) {
|
ScrollView(.horizontal) {
|
||||||
LazyHStack(spacing: 20) {
|
LazyHStack(spacing: 20) { chapterViews(for: chapters.prefix(3), opacity: 1.0) }.padding(.horizontal, 15)
|
||||||
ForEach(Array(chapters.indices), id: \.self) { index in
|
|
||||||
let chapter = chapters[index]
|
|
||||||
let nextChapterStart: Double? = index < chapters.count - 1 ? chapters[index + 1].start : nil
|
|
||||||
ChapterView(chapter: chapter, nextChapterStart: nextChapterStart, chapterIndex: index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 15)
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else if expand {
|
} else if expand {
|
||||||
|
#if os(tvOS)
|
||||||
Section {
|
Section {
|
||||||
ForEach(Array(chapters.indices), id: \.self) { index in
|
ForEach(chapters) { chapter in
|
||||||
let chapter = chapters[index]
|
ChapterViewTVOS(chapter: chapter)
|
||||||
let nextChapterStart: Double? = index < chapters.count - 1 ? chapters[index + 1].start : nil
|
|
||||||
ChapterView(chapter: chapter, nextChapterStart: nextChapterStart, chapterIndex: index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
#else
|
||||||
|
Section { chapterViews(for: chapters[...], opacity: 1.0) }.padding(.horizontal)
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
Button(action: {
|
Button(action: {
|
||||||
self.expand.toggle()
|
self.expand.toggle()
|
||||||
}) {
|
}) {
|
||||||
contents
|
Section {
|
||||||
|
chapterViews(for: chapters.prefix(3), opacity: 0.3)
|
||||||
|
}.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
#elseif os(macOS)
|
||||||
|
Section {
|
||||||
|
chapterViews(for: chapters.prefix(3), opacity: 0.3)
|
||||||
|
}.padding(.horizontal)
|
||||||
#else
|
#else
|
||||||
contents
|
Section {
|
||||||
|
ForEach(chapters) { chapter in
|
||||||
|
ChapterViewTVOS(chapter: chapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var contents: some View {
|
#if !os(tvOS)
|
||||||
Section {
|
private func chapterViews(for chaptersToShow: ArraySlice<Chapter>, opacity: Double = 1.0) -> some View {
|
||||||
ForEach(Array(chapters.prefix(3).indices), id: \.self) { index in
|
ForEach(Array(chaptersToShow.indices), id: \.self) { index in
|
||||||
let chapter = chapters[index]
|
let chapter = chaptersToShow[index]
|
||||||
let nextChapterStart: Double? = index < chapters.count - 1 ? chapters[index + 1].start : nil
|
let nextChapterStart: Double? = index < chaptersToShow.count - 1 ? chaptersToShow[index + 1].start : nil
|
||||||
ChapterView(chapter: chapter, nextChapterStart: nextChapterStart, chapterIndex: index)
|
ChapterView(chapter: chapter, nextChapterStart: nextChapterStart, chapterIndex: index)
|
||||||
.allowsHitTesting(expand)
|
.opacity(index == 0 ? 1.0 : opacity)
|
||||||
.opacity(index == 0 ? 1.0 : 0.3)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChaptersView_Previews: PreviewProvider {
|
struct ChaptersView_Previews: PreviewProvider {
|
||||||
|
@ -130,7 +130,7 @@ struct NowPlayingView: View {
|
|||||||
} else {
|
} else {
|
||||||
Section(header: Text("Chapters")) {
|
Section(header: Text("Chapters")) {
|
||||||
ForEach(video.chapters) { chapter in
|
ForEach(video.chapters) { chapter in
|
||||||
ChapterView(chapter: chapter)
|
ChapterViewTVOS(chapter: chapter)
|
||||||
.padding(.horizontal, 40)
|
.padding(.horizontal, 40)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user