Skip to content

Instantly share code, notes, and snippets.

@DDavis1025
Created June 3, 2020 17:21
Show Gist options
  • Save DDavis1025/731330c5fbb5cba6364b0f5566893f63 to your computer and use it in GitHub Desktop.
Save DDavis1025/731330c5fbb5cba6364b0f5566893f63 to your computer and use it in GitHub Desktop.
import UIKit
import AVFoundation
class VideoPlayerView: UIView {
private var playerLayer: AVPlayerLayer?
var player:AVPlayer?
var timer: Timer?
let activityIndicatorView:UIActivityIndicatorView = {
let aiv = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.large)
aiv.startAnimating()
return aiv
}()
lazy var pausePlayButton: UIButton = {
let button = UIButton(type: .system)
let config = UIImage.SymbolConfiguration(pointSize: 35, weight: .black, scale: .medium)
let image = UIImage(systemName: "pause.fill", withConfiguration: config) as UIImage?
let whiteImage = image?.withTintColor(.white, renderingMode: .alwaysOriginal)
button.setImage(whiteImage, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.isUserInteractionEnabled = true
button.isHidden = true
button.addTarget(self, action: #selector(handlePause(_:)), for: .touchUpInside)
return button
}()
var isPlaying = false
@objc func handlePause(_ sender: UIButton!) {
print("pause clicked")
if isPlaying {
player?.pause()
let button = UIButton(type: .system)
let config = UIImage.SymbolConfiguration(pointSize: 35, weight: .black, scale: .medium)
let play = UIImage(systemName: "play.fill", withConfiguration: config) as UIImage?
let playImage = play?.withTintColor(.white, renderingMode: .alwaysOriginal)
pausePlayButton.setImage(playImage, for: .normal)
} else {
let button = UIButton(type: .system)
let config = UIImage.SymbolConfiguration(pointSize: 35, weight: .black, scale: .medium)
let image = UIImage(systemName: "pause.fill", withConfiguration: config) as UIImage?
let whiteImage = image?.withTintColor(.white, renderingMode: .alwaysOriginal)
pausePlayButton.setImage(whiteImage, for: .normal)
player?.play()
}
isPlaying = !isPlaying
}
let controlsContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0, alpha: 1)
return view
}()
let videoLengthLabel:UILabel = {
let label = UILabel()
label.text = "00:00"
label.textColor = UIColor.white
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 13)
label.textAlignment = .right
return label
}()
let currentTimeLabel:UILabel = {
let label = UILabel()
label.text = "00:00"
label.textColor = UIColor.white
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 13)
label.textAlignment = .right
return label
}()
lazy var videoSlider: UISlider = {
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumTrackTintColor = UIColor.black
slider.maximumTrackTintColor = UIColor.white
let config = UIImage.SymbolConfiguration(pointSize: 17, weight: .black, scale: .medium)
let thumb = UIImage(systemName: "circle.fill", withConfiguration: config) as UIImage?
let thumbImage = thumb?.withTintColor(.black, renderingMode: .alwaysOriginal)
slider.setThumbImage(thumbImage, for: .normal)
slider.addTarget(self, action: #selector(handleSliderChange), for: .valueChanged)
return slider
}()
@objc func handleSliderChange() {
if let duration = player?.currentItem?.duration {
let totalSeconds = CMTimeGetSeconds(duration)
let value = Float64(videoSlider.value) * totalSeconds
let seekTime = CMTime(value: Int64(value), timescale: 1)
player?.seek(to: seekTime)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupPlayer()
controlsContainerView.isUserInteractionEnabled = true
addSubview(controlsContainerView)
controlsContainerView.addSubview(activityIndicatorView)
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
activityIndicatorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
controlsContainerView.addSubview(pausePlayButton)
pausePlayButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
pausePlayButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
pausePlayButton.widthAnchor.constraint(equalToConstant: 50).isActive = true
pausePlayButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
controlsContainerView.addSubview(videoLengthLabel)
videoLengthLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
videoLengthLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
videoLengthLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
videoLengthLabel.heightAnchor.constraint(equalToConstant: 24).isActive = true
controlsContainerView.addSubview(currentTimeLabel)
currentTimeLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: -8).isActive = true
currentTimeLabel.centerYAnchor.constraint(equalTo: videoLengthLabel.centerYAnchor).isActive = true
currentTimeLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
currentTimeLabel.heightAnchor.constraint(equalToConstant: 24).isActive = true
controlsContainerView.addSubview(videoSlider)
videoSlider.rightAnchor.constraint(equalTo: videoLengthLabel.leftAnchor, constant: -10).isActive = true
videoSlider.leftAnchor.constraint(equalTo: currentTimeLabel.rightAnchor, constant: 10).isActive = true
videoSlider.centerYAnchor.constraint(equalTo: videoLengthLabel.centerYAnchor).isActive = true
videoSlider.heightAnchor.constraint(equalToConstant: 30).isActive = true
backgroundColor = UIColor.black
}
private func setupPlayer() {
let urlString = "http://www.w3schools.com/html/mov_bbb.mp4"
guard let url = URL(string: urlString) else { return }
player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = .resizeAspectFill
self.layer.addSublayer(playerLayer)
self.playerLayer = playerLayer
player?.play()
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
//track player progress
let interval = CMTime(value: 1, timescale: 2)
player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { (progressTime) in
let seconds = CMTimeGetSeconds(progressTime)
let secondsString = String(format: "%02d", Int(seconds.truncatingRemainder(dividingBy: 60)))
let minutesString = String(format: "%02d", Int(seconds / 60))
self.currentTimeLabel.text = "\(minutesString):\(secondsString)"
if let duration = self.player?.currentItem?.duration {
let durationSeconds = CMTimeGetSeconds(duration)
self.videoSlider.value = Float(seconds / durationSeconds)
}
})
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "currentItem.loadedTimeRanges" {
print(change)
activityIndicatorView.stopAnimating()
controlsContainerView.backgroundColor = .clear
pausePlayButton.isHidden = false
isPlaying = true
if let duration = player?.currentItem?.duration {
let seconds = CMTimeGetSeconds(duration)
let secondsText = Int(seconds.truncatingRemainder(dividingBy: 60))
let minutesText = String(format:"%02d", Int(seconds) / 60)
videoLengthLabel.text = "\(minutesText):\(secondsText)"
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer?.frame = bounds
controlsContainerView.frame = bounds
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment