alexbuisson
alexbuisson

Reputation: 8499

SwiftUI VideoPlayer, how to add an overlay just on top of the active video area

Using SwiftUI I try to design a View composed of a VideoPlayer on which I would like to apply a "dynamic" UIImageView for the videoOverlay:, to simply the conception I use a png from my HD, but for real the UIImageView will come from an Observable shared object across all views

struct MyViewRepresentable: UIViewRepresentable {
typealias UIViewType = UIImageView
    
func updateUIView(_ uiView: UIImageView, context: Context) {
    
}

func makeUIView(context: Context) -> UIImageView {
    let imageView = UIImageView()
    imageView.image = UIImage(named: "tv-test-pattern.png")!
    imageView.alpha = 0.3
    return imageView
}

}

and I declare the player view as :

struct MyVideoPlayer: View {

//@ObservedObject var sharedData: SharedData

var body: some View {
    var v = VideoPlayer(player: AVPlayer(url: URL(string: "https://myurltoanvideo.mp4")!), videoOverlay: {
        GeometryReader { geometry in
        MyViewRepresentable()
            .frame(width: geometry.size.width, height: geometry.size.height)
            .clipped()
        }
    })
    v  
}
}

For now I the png is replicated over the whole view (including back area around the active video) but I would like to have it constraint to the active video area, in a old app swift + uikit designer we used an "AV Player view Controller" component and in the Viewcontroler of the App, we manually added the UIImageView as a subview with several NSLayoutContraint.

Is there a way to reproduce that behavior using "SwiftUI only" code, or do I have to use a RepresentableView based on AVPlayerViewControler ?

thanks in advance for all advices

Upvotes: 1

Views: 25

Answers (1)

malhal
malhal

Reputation: 30746

You can use @ObservedObject inside your representable and updateUIView will be called when the object changes. From there you can update the image, e.g.

struct MyViewRepresentable: UIViewRepresentable {
    @ObservedObject var sharedData: SharedData
    
    func updateUIView(_ uiView: UIImageView, context: Context) {
        if imageView.image.name != sharedData.imageName {
            imageView.image = UIImage(named: sharedData.imageName)!
        }
    }

    func makeUIView(context: Context) -> UIImageView {
        let imageView = UIImageView()
    
        imageView.alpha = 0.3
        return imageView
    }
}

Maybe you could change from UIKit constraints to SwiftUI and then just use Image? e.g.

struct MyVideoOverlay: View {

    @ObservedObject var sharedData: SharedData

    var body: some View {
        // use alignment or stacks with spacers
        Image(sharedData.imageName)
            .alpha(0.3)
            .frame( ...
    }
}

Also if that SharedData object is being passed down every View it could be easier to use EnvironmentObject so you can use it all Views without needing to pass it down as a let through ones that don't need it or to observe it.

Upvotes: 0

Related Questions