Search code examples
swiftswiftui

SwiftUI VideoPlayer, change default black background/side space


in SwiftUI I add a Videoplayer, but the frame is not really 16:9, but the video's are.

 private let player = AVPlayer(url: URL(string: UserDefaults.standard.string(forKey: "videoURL") ?? "https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4")!)
 

With this view:

ZStack {
    VideoPlayer(player: player)
       .onAppear() {
           player.play()
       }.frame(
            idealWidth: .infinity,
            maxWidth: .infinity,
            idealHeight: (UIScreen.main.bounds.height / 3) * 1.75,
            maxHeight: UIScreen.main.bounds.height - 600,
            alignment: .center
        )
    }
    .background(
        RoundedRectangle(cornerRadius: 40.0)
            .stroke(Color(.red), lineWidth: 5)
            .overlay(
                RoundedRectangle(cornerRadius: 40.0)
                    .fill(Color(.black))
            )
)

The Player is working and shown correctly, but with a black background on the both sides.. I want it red, so it does look prettier than random black in the red block.

Is there a way to change the background color of the black sideparts? I tried edgesIgnoringSafeArea with no luck.

Little but of how it looks like now, where you see the black "borders" on the side; see black side


Solution

  • Use AVPlayerLayer with the help of UIViewRepresentable and set the AVPlayerLayer layer color as you want.

    PlayerUIView

    class PlayerUIView: UIView {
        
        // MARK: Class Property
        
        let playerLayer = AVPlayerLayer()
        
        // MARK: Init
        
        override init(frame: CGRect) {
            super.init(frame: frame)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        init(player: AVPlayer) {
            super.init(frame: .zero)
            self.playerSetup(player: player)
        }
        
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
        
        // MARK: Life-Cycle
        
        override func layoutSubviews() {
            super.layoutSubviews()
            playerLayer.frame = bounds
            
        }
        
        // MARK: Class Methods
        
        private func playerSetup(player: AVPlayer) {
            playerLayer.player = player
            player.actionAtItemEnd = .none
            layer.addSublayer(playerLayer)
            playerLayer.backgroundColor = UIColor.red.cgColor // <--- Set color here
        }
    }
    

    PlayerView

    struct PlayerView: UIViewRepresentable {
        
        @Binding var player: AVPlayer
        
        func makeUIView(context: Context) -> PlayerUIView {
            return PlayerUIView(player: player)
        }
        
        func updateUIView(_ uiView: PlayerUIView, context: UIViewRepresentableContext<PlayerView>) {
            uiView.playerLayer.player = player
        }
    }
    

    Usage

    struct ContentView: View {
        @State private var player = AVPlayer(url: URL(string: UserDefaults.standard.string(forKey: "videoURL") ?? "https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4")!)
        
        var body: some View {
            ZStack {
                PlayerView(player: $player)
                    .onAppear() {
                        player.play()
                    }.ignoresSafeArea()
            }
        }
    }
    

    EDIT

    For video controller, use bellow code.

    struct PlayerView: UIViewRepresentable {
        
        @Binding var player: AVPlayer
        
        func makeUIView(context: Context) -> PlayerUIView {
            return PlayerUIView(player: player)
        }
        
        func updateUIView(_ uiView: PlayerUIView, context: UIViewRepresentableContext<PlayerView>) {
            uiView.playerViewController.player = player
        }
    }
    
    class PlayerUIView: UIView {
        
        // MARK: Class Property
        
        let playerViewController = AVPlayerViewController()
        
        // MARK: Init
        
        override init(frame: CGRect) {
            super.init(frame: frame)
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        init(player: AVPlayer) {
            super.init(frame: .zero)
            self.playerSetup(player: player)
        }
        
        // MARK: Life-Cycle
        
        override func layoutSubviews() {
            super.layoutSubviews()
            playerViewController.view.frame = bounds
        }
        
        // MARK: Class Methods
        
        private func playerSetup(player: AVPlayer) {
            playerViewController.player = player
            playerViewController.view.backgroundColor = .red
            self.addSubview(playerViewController.view)
        }
    }
    

    You also do not need to set the background view.