77
88import SwiftUI
99import AVFoundation
10+ import AVKit
1011
1112struct ContentView : View {
1213 @Environment ( \. dismiss) private var dismiss
@@ -15,6 +16,7 @@ struct ContentView: View {
1516 @State private var countdownTimer : Timer ?
1617 @State private var showDeleteConfirm : Bool = false
1718 @State private var segmentToDelete : CameraViewModel . RecordedSegment ? = nil
19+ @State private var previewSegment : CameraViewModel . RecordedSegment ?
1820 @State private var showFilterPicker : Bool = false
1921
2022 var body : some View {
@@ -214,30 +216,35 @@ struct ContentView: View {
214216 HStack ( spacing: 6 ) {
215217 ForEach ( model. segments) { seg in
216218 ZStack ( alignment: . topTrailing) {
217- Image ( uiImage: seg. thumbnail)
218- . resizable ( )
219- . scaledToFill ( )
220- . frame ( width: 34 , height: 44 )
221- . clipShape ( RoundedRectangle ( cornerRadius: 6 , style: . continuous) )
222- . overlay (
223- RoundedRectangle ( cornerRadius: 6 , style: . continuous)
224- . stroke ( Color . white. opacity ( 0.6 ) , lineWidth: 0.8 )
225- )
219+ Button {
220+ previewSegment = seg
221+ } label: {
222+ Image ( uiImage: seg. thumbnail)
223+ . resizable ( )
224+ . scaledToFill ( )
225+ . frame ( width: 44 , height: 58 )
226+ . clipShape ( RoundedRectangle ( cornerRadius: 8 , style: . continuous) )
227+ . overlay (
228+ RoundedRectangle ( cornerRadius: 8 , style: . continuous)
229+ . stroke ( Color . white. opacity ( 0.6 ) , lineWidth: 0.8 )
230+ )
231+ }
232+ . buttonStyle ( . plain)
226233
227234 Button ( action: {
228235 segmentToDelete = seg
229236 showDeleteConfirm = true
230237 } ) {
231238 Image ( systemName: " xmark " )
232- . font ( . system( size: 8 , weight: . bold) )
239+ . font ( . system( size: 7 , weight: . bold) )
233240 . foregroundColor ( . white)
234- . frame ( width: 14 , height: 14 )
241+ . frame ( width: 12 , height: 12 )
235242 . background ( Color . black. opacity ( 0.75 ) )
236243 . clipShape ( Circle ( ) )
237244 }
238245 . padding ( 2 )
239246 }
240- . frame ( width: 34 , height: 44 )
247+ . frame ( width: 44 , height: 58 )
241248 }
242249 }
243250 . padding ( . horizontal, 10 )
@@ -336,6 +343,18 @@ struct ContentView: View {
336343 . onChange ( of: model. isTeleprompterOn) { _ in
337344 withAnimation ( . easeOut( duration: 0.2 ) ) { showFilterPicker = false }
338345 }
346+ . sheet ( item: $previewSegment) { segment in
347+ SegmentPlaybackView (
348+ segment: segment,
349+ onDelete: {
350+ model. deleteSegment ( segment)
351+ previewSegment = nil
352+ } ,
353+ onClose: {
354+ previewSegment = nil
355+ }
356+ )
357+ }
339358 . alert ( " Deseja apagar esse take? " , isPresented: $showDeleteConfirm) {
340359 Button ( " Apagar " , role: . destructive) {
341360 if let seg = segmentToDelete {
@@ -422,6 +441,72 @@ extension ContentView {
422441 }
423442}
424443
444+ struct SegmentPlaybackView : View {
445+ let segment : CameraViewModel . RecordedSegment
446+ let onDelete : ( ) -> Void
447+ let onClose : ( ) -> Void
448+
449+ @State private var player = AVPlayer ( )
450+ @State private var showDeleteDialog = false
451+
452+ var body : some View {
453+ NavigationStack {
454+ ZStack {
455+ Color . black
456+ . ignoresSafeArea ( )
457+
458+ VStack ( spacing: 24 ) {
459+ VideoPlayer ( player: player)
460+ . aspectRatio ( 9.0 / 16.0 , contentMode: . fit)
461+ . clipShape ( RoundedRectangle ( cornerRadius: 18 , style: . continuous) )
462+ . shadow ( color: Color . black. opacity ( 0.4 ) , radius: 18 , x: 0 , y: 12 )
463+
464+ Button ( role: . destructive) {
465+ showDeleteDialog = true
466+ } label: {
467+ Label ( " Apagar take " , systemImage: " trash " )
468+ . font ( . system( size: 17 , weight: . semibold) )
469+ . padding ( . vertical, 12 )
470+ . frame ( maxWidth: . infinity)
471+ }
472+ . buttonStyle ( . borderedProminent)
473+ . tint ( . red)
474+
475+ Spacer ( )
476+ }
477+ . padding ( )
478+ }
479+ . navigationTitle ( " Pré-visualização " )
480+ . navigationBarTitleDisplayMode ( . inline)
481+ . toolbar {
482+ ToolbarItem ( placement: . topBarLeading) {
483+ Button ( " Fechar " , action: onClose)
484+ }
485+ }
486+ . confirmationDialog (
487+ " Apagar este take? " ,
488+ isPresented: $showDeleteDialog,
489+ titleVisibility: . visible
490+ ) {
491+ Button ( " Apagar " , role: . destructive) {
492+ player. pause ( )
493+ onDelete ( )
494+ }
495+ Button ( " Cancelar " , role: . cancel) { }
496+ }
497+ }
498+ . interactiveDismissDisabled ( true )
499+ . onAppear {
500+ player. replaceCurrentItem ( with: AVPlayerItem ( url: segment. url) )
501+ player. play ( )
502+ }
503+ . onDisappear {
504+ player. pause ( )
505+ player. replaceCurrentItem ( with: nil )
506+ }
507+ }
508+ }
509+
425510struct FilterMenu : View {
426511 let selected : CameraViewModel . VideoFilter
427512 let onSelect : ( CameraViewModel . VideoFilter ) -> Void
0 commit comments