devlo
devlo

Reputation: 363

Playback controls in swiftui

Attempting to use AVKit's AVPlayer to play a video without playback controls:

private let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "videoToPlay", ofType: "mp4")!))
player.showsPlaybackControls = false

The above results in an error message that declarations must be separated by ';'. I've also tried:

VideoPlayer(player: player)
    .onAppear {
        player.showsPlaybackControls = false
    }

Which results in a different error.

Any advice on hiding playback controls with swiftui?

Upvotes: 20

Views: 9254

Answers (6)

ugur
ugur

Reputation: 3654

If you want to hide system-provided playback controls and still support tap gesture, you can use the following approach.

ZStack {
    VideoPlayer(player: model.player)
        .disabled(true)

    Button {
        isPlaying.toggle()
    } label: {
        Color.clear
    }
}

Upvotes: -1

Colin
Colin

Reputation: 121

For me, setting allowsHitTesting false hides play back controls in iOS 16:

private let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "videoToPlay", ofType: "mp4")!))

...

// Disable user interaction on the video player to hide playback controls
VideoPlayer(player: player)
   .allowsHitTesting(false)

There may be some corner cases I'm missing where the UI could still appear, but I have yet to encounter that.

Upvotes: 11

Andrew
Andrew

Reputation: 11337

import Foundation
import SwiftUI
import AVKit
import AVFoundation

struct GifyVideoView : View {
    let videoName: String
    
    var body: some View {
        if let url = Bundle.main.url(forResource: videoName, withExtension: "mp4") {
            GifyVideoViewWrapped(url: url)
        } else {
            Text("No video for some reason")
        }
    }
}

fileprivate struct GifyVideoViewWrapped: View {
    let player : AVPlayer
    
    var pub = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
    
    init(url: URL) {
        player = AVPlayer(url: url)
    }
    
    var body: some View {
        AVPlayerControllerRepresented(player: player)
            .onAppear {
                player.play()
            }
            .onReceive(pub) { (output) in
                player.play()
            }
    }
}

fileprivate struct AVPlayerControllerRepresented : NSViewRepresentable {
    var player : AVPlayer
    
    func makeNSView(context: Context) -> AVPlayerView {
        let view = AVPlayerView()
        view.controlsStyle = .none
        view.player = player
        return view
    }
    
    func updateNSView(_ nsView: AVPlayerView, context: Context) { }
}

usage:

GifyVideoView(VideoName: "someMp4VideoName")
    .frame(width: 314, height: 196 * 2)
    .clipShape( RoundedRectangle(cornerRadius: 15, style: .continuous) )

video will be played looped ant without controls.

Video will be taken from project resources

enter image description here

Upvotes: 0

cora
cora

Reputation: 2102

I was having problems with playing segments with VideoPlayer, so I had to resort to using AVPlayerLayer which does not have the controls.

class PlayerView: UIView {

    // Override the property to make AVPlayerLayer the view's backing layer.
    override static var layerClass: AnyClass { AVPlayerLayer.self }
    
    // The associated player object.
    var player: AVPlayer? {
        get { playerLayer.player }
        set { playerLayer.player = newValue }
    }
    
    private var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer }
}

struct CustomVideoPlayer: UIViewRepresentable {
    let player: AVQueuePlayer

    func makeUIView(context: Context) -> PlayerView {
        let view = PlayerView()
        view.player = player
        return view
    }
    
    func updateUIView(_ uiView: PlayerView, context: Context) { }
}

Usage:

CustomVideoPlayer(player: player)
                .onAppear() {
                    player.play()
                }
                .onDisappear() {
                    player.pause()
                }
                .ignoresSafeArea(.all)

Upvotes: 1

Senseful
Senseful

Reputation: 91911

Another great option is to use a video layer instead of an AVPlayerViewController. This is especially useful if the video needs to repeat, for example, since that seems to be the only way to support that use-case.

Since you're just displaying a video layer, there are no controls by default.

How to loop video with AVPlayerLooper

Upvotes: -1

jnpdx
jnpdx

Reputation: 52575

VideoPlayer does not appear to have any methods on it that I can find in the documentation to control whether the playback controls are shown.

showPlaybackControls doesn't work because AVPlayer doesn't have that property either.

It looks like for now you'll have to do something like wrap an AVPlayerViewController:

Note that this is a very limited example and doesn't take into account a lot of scenarios you may need to consider, like what happens when AVPlayerControllerRepresented gets reloaded because it's parent changes -- will you need to use updateUIViewController to update its properties? You will also probably need a more stable solution to storing your AVPlayer than what I used, which will also get recreated any time the parent view changes. But, all of these are relatively easily solved architectural decisions (look into ObservableObject, StateObject, Equatable, etc)

struct ContentView: View {
    let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "IMG_0226", ofType: "mp4")!))
    var body: some View {
        AVPlayerControllerRepresented(player: player)
            .onAppear {
                player.play()
            }
    }
}

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) {
        
    }
}

Upvotes: 27

Related Questions