Add Custom Controls to Avplayer in Swift

Add custom controls to AVPlayer in swift

here I add the points , you need to customize based on your need.

Step-1

initially hide your AVPlayer controls,

 YourAVPlayerViewController.showsPlaybackControls = false

Step-2

create the structure like

Sample Image

one label for current Duration, One label for overall Duration, one UIbutton for pause and play your current player and one UISlider for seek The video.

step-3

initially close the simple steps.

first stop and play the player using button action , currentPlayer is your AVPlayer name.

@IBAction func handlePlayPauseButtonPressed(_ sender: UIButton) {
// sender.isSelected ? currentPlayer.pause() : currentPlayer.play()
if sender.isSelected {
currentPlayer.pause()
}
else {
currentPlayer.play()
}
}

second set the video duration, as like

    let duration : CMTime = currentPlayer.currentItem!.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)

lblOverallDuration.text = self.stringFromTimeInterval(interval: seconds)

third set the player current time to current duration label

    let duration : CMTime = currentPlayer.currentTime()
let seconds : Float64 = CMTimeGetSeconds(duration)

lblcurrentText.text = self.stringFromTimeInterval(interval: seconds)

the following method is convert from NSTimeinterval to HH:MM:SS

func stringFromTimeInterval(interval: TimeInterval) -> String {

let interval = Int(interval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
let hours = (interval / 3600)
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}

finally we go for slider control for calulate the seek time

_playheadSlider.addTarget(self, action: #selector(self.handlePlayheadSliderTouchBegin), for: .touchDown)
_playheadSlider.addTarget(self, action: #selector(self.handlePlayheadSliderTouchEnd), for: .touchUpInside)
_playheadSlider.addTarget(self, action: #selector(self.handlePlayheadSliderTouchEnd), for: .touchUpOutside)
_playheadSlider.addTarget(self, action: #selector(self.handlePlayheadSliderValueChanged), for: .valueChanged)

lets we go for action, initially when touchbegin is start then stop the player

handlePlayheadSliderTouchBegin

@IBAction func handlePlayheadSliderTouchBegin(_ sender: UISlider) {
currentPlayer.pause()
}

set the current item label for calculate the sender.value * CMTimeGetSeconds(currentPlayer.currentItem.duration)

@IBAction func handlePlayheadSliderValueChanged(_ sender: UISlider) {

let duration : CMTime = currentPlayer.currentItem!.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration) * sender.value
// var newCurrentTime: TimeInterval = sender.value * CMTimeGetSeconds(currentPlayer.currentItem.duration)
lblcurrentText.text = self.stringFromTimeInterval(interval: seconds)
}

finally move the player based on seek

 @IBAction func handlePlayheadSliderTouchEnd(_ sender: UISlider) {

let duration : CMTime = currentPlayer.currentItem!.asset.duration
var newCurrentTime: TimeInterval = sender.value * CMTimeGetSeconds(duration)
var seekToTime: CMTime = CMTimeMakeWithSeconds(newCurrentTime, 600)
currentPlayer.seek(toTime: seekToTime)
}

iOS - Adding controls on top of AVPlayerViewController

I solved the issue by adding the following two lines alongside the addSubview function:

self.addChildViewController(playerViewController)
playerView.addSubview(playerViewController.view)
playerViewController.didMove(toParentViewController: self)

Custom control for playback speed for AVPlayerViewController


  1. Can we add custom playback speed (0.5, 1.0, 1.5, 2.0) control to AVPlayerViewController?

You can add a custom playback speed button to AVPlayerViewController by doing a little hack.

In iOS 11, i.e., you can add a button to the AVPlayerViewController by adding the control next to the "Play/Pause" button. That button has an Accessible Identifier which will help you find that control.

iOS 11 AVPlayerViewController

So, what I did was basically find that "Play/Pause" button and add my Playback Speed button to the superview of the "Play/Pause" button.

func findPlayPauseButton(view: UIView) -> UIView? {
if view.accessibilityIdentifier == "Play/Pause" {
return view
}
for subview in view.subviews {
if let result = findPlayPauseButton(view: subview) {
return result
}
}
return nil
}

guard let playerView = playerViewController?.view else {
return
}

guard let playPauseButton = findPlayPauseButton(view: playerView) else {
return
}

if let playerControlsView = playPauseButton.superview {
let playbackSpeedButton = UIButton(type: .system)
playbackSpeedButton.accessibilityIdentifier = "PlaybackSpeed"
playbackSpeedButton.setTitle("1 X", for: .normal)
playbackSpeedButton.addTarget(self, action: #selector(self.playbackSpeedButtonTapped), for: .touchUpInside)
playbackSpeedButton.translatesAutoresizingMaskIntoConstraints = false
playerControlsView.addSubview(playbackSpeedButton)
NSLayoutConstraint.activate([
playbackSpeedButton.heightAnchor.constraint(equalTo: playPauseButton.heightAnchor),
playbackSpeedButton.widthAnchor.constraint(equalToConstant: 50),
playbackSpeedButton.leadingAnchor.constraint(equalTo: playPauseButton.trailingAnchor, constant: 70),
playbackSpeedButton.bottomAnchor.constraint(equalTo: playPauseButton.bottomAnchor),
])
}

Result:

Playback Speed Button Example

As you may know, this has a lot of downsides because of all the edge cases that you will find, i.e., you need to take care the landscape as well, you need to check if the hack is still valid on every new iOS version, etc. If you app supports iOS 10, then things are different because the UI of the AVPlayerViewController has changed in iOS 11.


  1. Is there a way we can refresh speed rate using an UISlider value change?

Yes, instead of a UIButton for changing the playback speed, just add an UISlider.


  1. Can we change the playback speed if it is playing currently?

Yes, if it's playing then you just need to change the rate property of the player: for example to 1.25 X => playerViewController.player?.rate = 1.25.

UPDATE:

Enhancement requested! Radar number is 46022646: Playback speed control for AVPlayerViewController (Video).

Can AVPlayerViewController be customized via Storyboard or must it be done via adding subviews?

The short answer as of now, it is not possible to work and customize AVPlayerViewController in your storyboard.

You probably added a UIViewController in your storyboard and set the class of the UIViewController to AVPlayerViewController and probably the AVPlayerViewController's implementation is not prepared to handle this.

For now, you cannot add an AVPlayerViewController to the storyboard and move the controls around or add your subviews.

The closest thing you can do to working with a view in storyboard is to subclass AVPlayerViewController

class CustomAVPlayerController: AVPlayerViewController
{
// your implementation
}

And you can work with this in your storyboard provided you implement required init?(coder: NSCoder)

However, tread this path with caution as Apple recommends against subclassing AVPlayerViewController.

Overview

A player view controller makes it simple to add media
playback capabilities to your app that match the styling and features
of the native system players. Using this object also means that your
app automatically adopts the new features and styling of future
operating system releases.

AVPlayerViewController subclass

I would say use this only if you have to do some basic work like detecting orientation changes, add some colors to views etc and not to mess around with subviews and controls.

If you only work with MP3s you can get away with playing your audio using an AVPlayer in a normal UIViewController rather AVPlayerViewController and this is what I did for one of the apps I developed - Reliable Radio

Here is an example of a UI I was able to achieve:

Custom AVPlayerViewController with subviews AVPlayer play MP3 https://apps.apple.com/us/app/reliable-radio/id1565774051

The pro of the above approach is that you can customize the view as you like, the con of that approach is you have to develop all the controls like play, forward, rewind, volume, scrubber etc

Here is an idea on how to achieve that

So it's a choice on what you are willing to compromise:

  1. Not much UI customization but all the controls for free - AVPlayerViewController
  2. Full UI control but develop your own controls - AVPlayer contained in a custom UIViewController

I will also answer your second question shortly about the UITextView scroll issue with a solution that is somewhere halfway between 1 and 2 above

MPMoviewcontroller and AVPlayer with custom control

I have the same issue with MPMoviePlayerController. Also We can't add more than one MPMoviePlayerController in a same view. For this, I have created a custom player using AVPlayer having basic features. You can download it from my github account HRVideoPlayer. Let me know if you want more detailed version. I have built my own video player having all functionalities like Seeking, volume control, brightness control, replace currentItem, Repeat and shuffle.

AVPlayer with playback controls of avplayerviewcontroller

No. The usual solution (explicitly advised by Apple) is to use the AVPlayerViewController as an embedded view controller - i.e., make your view controller a custom parent view controller and the AVPlayerViewController its child, and now you can place its view (the movie and the controls) inside your view as a subview in good order. Example (self is your view controller):

let url = // whatever
let player = AVPlayer(URL:url)
let avPlayerViewController = AVPlayerViewController()
avPlayerViewController.player = player
avPlayerViewController.view.frame = // whatever
self.addChild(avPlayerViewController)
self.view.addSubview(avPlayerViewController.view)
avPlayerViewController.didMove(toParent: self)


Related Topics



Leave a reply



Submit