green8
green8

Reputation: 496

SwiftUI: Play video only if it's in the centre of the screen

Hi am making app which plays video only If it's in the centre of the view. I've already managed to get the position in the scroll view, but I can't combine that with playing the video.

This is my main view:

struct MainView: View {
    @State var position = 0.0
    var body: some View {
        ScrollView {
            ForEach(videos){ video in
                VideoView(player: video.player)
                    .onChange(of: position) { pos in
                        if pos > -50 && pos < 400 {
                            print("Play video")
                        }else {
                            print("Stop video")
                        }
                    }
            }
            .background(GeometryReader {
                Color.clear.preference(key: ViewOffsetKey.self, value: -$0.frame(in: .named("scroll")).origin.y)
            })
            
        }.onPreferenceChange(ViewOffsetKey.self) {
            position = $0
        }
        .coordinateSpace(name: "scroll")
        .padding()
    }
}

This is my video model:

struct VideoModel: Identifiable {
    var id = UUID()
    var number: Int
    var player: AVPlayer
}

This is video array:

let videos = [
    VideoModel(number: 1, player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "video", ofType: "mp4")!))),
    VideoModel(number: 3, player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "video", ofType: "mp4")!))),
    VideoModel(number: 4, player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "video", ofType: "mp4")!))),
    VideoModel(number: 5, player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "video", ofType: "mp4")!)))
]

And those are structures handling the video player and preference key:

struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

struct VideoView: View {
    var player: AVPlayer
    var body: some View {
        AVPlayerControllerRepresented(player: player)
            .frame(height: height)
    }
}

struct AVPlayerControllerRepresented : UIViewControllerRepresentable {
    var player : AVPlayer
    
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let controller = AVPlayerViewController()
        controller.player = player
        controller.showsPlaybackControls = false
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
        
    }
}

Please help, I will be so thankful.

Upvotes: 1

Views: 2030

Answers (1)

RelativeJoe
RelativeJoe

Reputation: 5094

First You need to make the AVPlayer a Binding for the changes in VideoView & AVPlayerControllerRepresented to take effect, then add those pieces of code accordingly

player.play() // to play
player.stop() // to stop

Upvotes: 2

Related Questions