Video displaying black while audio plays in the background on Apple Vision Pro simulator

When I try to play video on my Apple Vision Pro simulator using a custom view with an AVPlayerLayer (as seen in my below VideoPlayerView), nothing displays but a black screen while the audio for the video i'm trying to play plays in the background. I've tried everything I can think of to resolve this issue, but to no avail.

import SwiftUI
import AVFoundation
import AVKit

struct VideoPlayerView: UIViewRepresentable {
    var player: AVPlayer

    func makeUIView(context: Context) -> UIView {
        let view = UIView(frame: .zero)

        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.videoGravity = .resizeAspect
        view.layer.addSublayer(playerLayer)

        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        if let layer = uiView.layer.sublayers?.first as? AVPlayerLayer {
            layer.frame = uiView.bounds
        }
    }
}

I have noticed however that if i use the default VideoPlayer (as demonstrated below), and not my custom VideoPlayerView, the video displays just fine, but any modifiers I use on that VideoPlayer (like the ones in my above custom struct), cause the video to display black while the audio plays in the background.

import SwiftUI
import AVKit

struct MyView: View {
    var player: AVPlayer
    
    var body: some View {
        ZStack {
            VideoPlayer(player: player)

Does anyone know a solution to this problem to make it so that video is able to display properly and not just appear as a black screen with audio playing in the background?

Accepted Reply

SOLVED:

It took me literal months to figure this out but alas, there is actually a solution to this problem. If you ever use an AVPlayer in a visionOS app and find that your video is displaying as a black screen whilst the audio plays in the background, use some variant of this implementation below.

The solution: create a custom view for your AVPlayer as is done below

import SwiftUI
import AVKit

// Define a UIView subclass to host the AVPlayerLayer
class PlayerView: UIView {
    private var playerLayer = AVPlayerLayer()

    // Initialize with an AVPlayer
    init(player: AVPlayer) {
        super.init(frame: .zero)
        playerLayer.player = player
        playerLayer.videoGravity = .resizeAspect // Adjust as needed
        layer.addSublayer(playerLayer)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

// UIViewRepresentable wrapper for the PlayerView
struct VideoPlayerView: UIViewRepresentable {
    var player: AVPlayer

    func makeUIView(context: Context) -> PlayerView {
        return PlayerView(player: player)
    }

    func updateUIView(_ uiView: PlayerView, context: Context) {
        // No need to update anything for now; the PlayerView handles resizing
    }
}

Then use this custom view in your program wherever needed like this:

struct ContentView: View {
    var player: AVPlayer

    var body: some View {
        VideoPlayerView(player: player)
    }
}

Replies

Something that jumps out to me is you have the view frame set to zero. You might want to set it to use constraints.

Yea I suspected this too, so i ended up trying a few implementations without (frame: .zero), and with different constraints, but still experienced the same black screen issue. I've gone as far as testing empty makeUIView and updateUIView functions, and the issue prevailed. Seems as though any implementation of AVPlayer in a UIViewRepresentable will create this issue.

Just came across this sample code that uses a custom AVPlayer view via the PlayerModel and SystemPlayerView classes:

https://developer.apple.com/documentation/visionos/destination-video

Maybe that helps.

SOLVED:

It took me literal months to figure this out but alas, there is actually a solution to this problem. If you ever use an AVPlayer in a visionOS app and find that your video is displaying as a black screen whilst the audio plays in the background, use some variant of this implementation below.

The solution: create a custom view for your AVPlayer as is done below

import SwiftUI
import AVKit

// Define a UIView subclass to host the AVPlayerLayer
class PlayerView: UIView {
    private var playerLayer = AVPlayerLayer()

    // Initialize with an AVPlayer
    init(player: AVPlayer) {
        super.init(frame: .zero)
        playerLayer.player = player
        playerLayer.videoGravity = .resizeAspect // Adjust as needed
        layer.addSublayer(playerLayer)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

// UIViewRepresentable wrapper for the PlayerView
struct VideoPlayerView: UIViewRepresentable {
    var player: AVPlayer

    func makeUIView(context: Context) -> PlayerView {
        return PlayerView(player: player)
    }

    func updateUIView(_ uiView: PlayerView, context: Context) {
        // No need to update anything for now; the PlayerView handles resizing
    }
}

Then use this custom view in your program wherever needed like this:

struct ContentView: View {
    var player: AVPlayer

    var body: some View {
        VideoPlayerView(player: player)
    }
}