@@ -22,8 +22,7 @@ struct BuilderVideo: BuilderViewProtocol {
22
22
23
23
// AVPlayer
24
24
@State private var player : AVPlayer ?
25
- @State private var playerLayer : AVPlayerLayer ?
26
- // New state to manage play button visibility and video playing state
25
+
27
26
@State private var showOverlay : Bool
28
27
@State private var isPlaying : Bool = false
29
28
@@ -76,31 +75,45 @@ struct BuilderVideo: BuilderViewProtocol {
76
75
var body : some View {
77
76
Group {
78
77
if let videoURL = videoURL {
79
- VideoPlayer ( player: player)
80
- . onAppear {
81
- player = AVPlayer ( url: videoURL)
82
- playerLayer = AVPlayerLayer ( player: player)
83
- playerLayer? . videoGravity =
84
- self . contentMode == . fill ? . resizeAspectFill : . resizeAspect
85
- player? . isMuted = muted
86
-
87
- if loop {
88
- NotificationCenter . default. addObserver (
89
- forName: . AVPlayerItemDidPlayToEndTime, object: player? . currentItem, queue: . main
90
- ) { [ self ] _ in
91
- self . player? . seek ( to: CMTime . zero)
92
- self . player? . play ( )
93
- }
94
- }
95
-
96
- if autoPlay {
97
- self . showOverlay = false
98
- player? . play ( )
99
- }
78
+ Rectangle ( ) . fill ( Color . clear)
79
+ . aspectRatio ( self . aspectRatio ?? 1 , contentMode: self . contentMode)
80
+ . if ( contentMode == . fill) { view in
81
+ view. background (
82
+ VideoPlayerView (
83
+ videoURL: videoURL,
84
+ autoPlay: true ,
85
+ muted: self . muted,
86
+ loop: self . loop
87
+ )
88
+ )
100
89
}
101
- . onDisappear {
102
- player? . pause ( )
103
- player = nil
90
+ . if ( contentMode == . fit) { view in
91
+ view. background (
92
+ VideoPlayer ( player: player)
93
+ . onAppear {
94
+ player = AVPlayer ( url: videoURL)
95
+ player? . isMuted = muted
96
+
97
+ if loop {
98
+ NotificationCenter . default. addObserver (
99
+ forName: . AVPlayerItemDidPlayToEndTime, object: player? . currentItem,
100
+ queue: . main
101
+ ) { [ self ] _ in
102
+ self . player? . seek ( to: CMTime . zero)
103
+ self . player? . play ( )
104
+ }
105
+ }
106
+
107
+ if autoPlay {
108
+ self . showOverlay = false
109
+ player? . play ( )
110
+ }
111
+ } . onDisappear {
112
+ player? . pause ( )
113
+ player = nil
114
+ }
115
+
116
+ )
104
117
}
105
118
. aspectRatio ( aspectRatio, contentMode: self . contentMode)
106
119
. overlay (
@@ -141,9 +154,6 @@ struct BuilderVideo: BuilderViewProtocol {
141
154
}
142
155
}
143
156
)
144
- . onChange ( of: muted) { newMuted in
145
- player? . isMuted = newMuted
146
- }
147
157
148
158
} else {
149
159
// If no video URL, display poster image or an empty view
@@ -152,3 +162,72 @@ struct BuilderVideo: BuilderViewProtocol {
152
162
}
153
163
}
154
164
}
165
+
166
+ struct VideoPlayerView : UIViewRepresentable {
167
+ var videoURL : URL
168
+ var autoPlay : Bool
169
+ var muted : Bool
170
+ var loop : Bool
171
+
172
+ func makeUIView( context: Context ) -> PlayerContainerView {
173
+ let playerContainerView = PlayerContainerView (
174
+ videoURL: videoURL,
175
+ autoPlay: autoPlay,
176
+ muted: muted,
177
+ loop: loop
178
+ )
179
+ return playerContainerView
180
+ }
181
+
182
+ func updateUIView( _ uiView: PlayerContainerView , context: Context ) {
183
+
184
+ uiView. player? . isMuted = muted
185
+
186
+ }
187
+
188
+ // 2. Custom UIView subclass to host the AVPlayerLayer
189
+ class PlayerContainerView : UIView {
190
+ var player : AVPlayer ? // Make it optional to handle init scenarios
191
+ private var playerLayer : AVPlayerLayer !
192
+ private var playerLooper : AVPlayerLooper ?
193
+
194
+ init ( videoURL: URL , autoPlay: Bool , muted: Bool , loop: Bool ) {
195
+ super. init ( frame: . zero)
196
+ self . backgroundColor = . black
197
+
198
+ // For seamless looping, use AVPlayerLooper with a queue player
199
+ // This also handles the initial autoPlay.
200
+ let playerItem = AVPlayerItem ( url: videoURL)
201
+ let queuePlayer = AVQueuePlayer ( playerItem: playerItem)
202
+
203
+ player = queuePlayer // Assign to the optional player property
204
+ playerLayer = AVPlayerLayer ( player: queuePlayer)
205
+ playerLayer. videoGravity = . resizeAspectFill
206
+
207
+ self . layer. addSublayer ( playerLayer)
208
+
209
+ // Apply mute setting
210
+ player? . isMuted = muted
211
+
212
+ // Handle looping
213
+ if loop {
214
+ playerLooper = AVPlayerLooper ( player: queuePlayer, templateItem: playerItem)
215
+ }
216
+
217
+ // Handle autoPlay
218
+ if autoPlay {
219
+ queuePlayer. play ( )
220
+ }
221
+
222
+ }
223
+
224
+ required init ? ( coder: NSCoder ) {
225
+ fatalError ( " init(coder:) has not been implemented " )
226
+ }
227
+
228
+ override func layoutSubviews( ) {
229
+ super. layoutSubviews ( )
230
+ playerLayer. frame = bounds
231
+ }
232
+ }
233
+ }
0 commit comments