-
Notifications
You must be signed in to change notification settings - Fork 9
/
ContentView.swift
143 lines (123 loc) · 6.59 KB
/
ContentView.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//
// ContentView.swift
// Bilibili-Example
//
// Created by shayanbo on 2023/6/29.
//
import SwiftUI
import AVKit
import VideoPlayerContainer
struct ContentView: View {
/// create Context and let it have the same lifecycle with its enclosing underlying view
@StateObject var context = Context()
/// we need hold the orientation as @State to update the whole UI when it changes
@State var orientation = UIDevice.current.orientation
var body: some View {
GeometryReader { proxy in
VStack {
PlayerWidget(context)
.frame(maxHeight: orientation.isLandscape ? .infinity : proxy.size.width * 0.5625)
/// background modifier will cover the top safe area which make the VideoPlayerContainer looks better
.background(.black)
/// observe the device orientation and update status accordingly
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification), perform: { _ in
self.orientation = UIDevice.current.orientation
if UIDevice.current.orientation.isLandscape {
/// update the status to Fullscreen, it will trigger the Control overlay UI updates. removing the Halfscreen widgets and present fullscreen widgets
context.status.toFullScreen()
} else {
/// update the status to Halfscreen, it will trigger the Control overlay UI updates. removing the fullscreen widgets and present portrait widgets
context.status.toHalfScreen()
}
})
.onAppear {
/// setup show/dismiss transition for halfscreen and fullscreen
context.control.configure([
.halfScreen(.bottom), .halfScreen(.top),
.fullScreen(.bottom), .fullScreen(.top),
.fullScreen(.left), .fullScreen(.right)
], transition: .opacity)
/// setup insets for the whole Control overlay, since we would like to leave some space at the edges to make it looks better
context.control.configure(.halfScreen, insets: .init(top: 10, leading: 5, bottom: 10, trailing: 5))
context.control.configure(.fullScreen, insets: .init(top: 0, leading: 60, bottom: 34, trailing: 60))
/// widgets for highest-top location in halfscreen status
context.control.configure(.halfScreen(.top1)) {[
BackWidget(),
SpacerWidget(),
MusicWidget(),
AirplayWidget(),
MoreWidget(),
]}
/// widgets for lowest-bottom location in halfscreen status
context.control.configure(.halfScreen(.bottom1)) {[
PlaybackWidget(),
SeekBarWidget(),
TimelineWidget(),
FullscreenWidget(),
]}
/// widgets for highest-top location in fullscreen status
context.control.configure(.fullScreen(.top1)) {[
SpacerWidget(),
ClockWidget(),
SpacerWidget(),
NetworkWidget(),
BatteryWidget(),
]}
/// widgets for lowest-top location in fullscreen status
context.control.configure(.fullScreen(.top2)) {[
BackWidget(),
TitleWidget(),
SpacerWidget(),
ThumbUpWidget(),
ThumbDownWidget(),
CoinWidget(),
ChargeWidget(),
ShareWidget(),
MoreWidget(),
]}
/// widgets for lowest-bottom location in fullscreen status
context.control.configure(.fullScreen(.bottom1)) {[
PlaybackWidget(),
WhateverWidget1(),
WhateverWidget2(),
DanmakuWidget(),
CaptionWidget(),
SpeedWidget(),
QualityWidget(),
]}
/// widgets for medium-bottom location in fullscreen status
context.control.configure(.fullScreen(.bottom2)) {[
TimelineWidget(),
SeekBarWidget(),
DurationWidget(),
]}
/// widgets for right side in fullscreen status
context.control.configure(.fullScreen(.right)) {[
GiftWidget(),
SnapshotWidget(),
]}
/// widgets for left side in fullscreen status
context.control.configure(.fullScreen(.left)) {[
FollowWidget(),
SpacerWidget(),
]}
/// play the demo video
let item = AVPlayerItem(url: Bundle.main.url(forResource: "demo", withExtension: "mp4")!)
context.render.player.replaceCurrentItem(with: item)
context.render.player.play()
}
/// fill up the remaining space for portrait
if !orientation.isLandscape {
Rectangle().fill(.white)
}
}
}
.ignoresSafeArea(edges: orientation.isLandscape ? .all : .bottom)
.preferredColorScheme(.dark)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}