Skip to content

Instantly share code, notes, and snippets.

@DDavis1025
Created June 4, 2020 00:15
Show Gist options
  • Save DDavis1025/5932d7576e4d0fe59505c91943d89463 to your computer and use it in GitHub Desktop.
Save DDavis1025/5932d7576e4d0fe59505c91943d89463 to your computer and use it in GitHub Desktop.
import UIKit
import AVFoundation
class VideoPlayerView: UIView {
private var playerLayer: AVPlayerLayer?
var player:AVPlayer?
var isPlaying = false
let activityIndicatorView:UIActivityIndicatorView = {
let aiv = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.large)
aiv.translatesAutoresizingMaskIntoConstraints = false
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
}()
@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 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() {
print(videoSlider.value)
if let duration = player?.currentItem?.duration{
let totalSeconds = CMTimeGetSeconds(duration)
let value = Float64(videoSlider.value) * totalSeconds
print("value \(value)")
let seekTime = CMTime(value: Int64(value), timescale: 1)
print("seekTime \(seekTime)")
player?.seek(to: seekTime, completionHandler: { (completedSeek) in
})
}
}
let controlsContainerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor(white: 0, alpha: 1)
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupPlayer()
addSubview(controlsContainerView)
NSLayoutConstraint.activate([
controlsContainerView.topAnchor.constraint(equalTo: topAnchor),
controlsContainerView.bottomAnchor.constraint(equalTo: bottomAnchor),
controlsContainerView.leftAnchor.constraint(equalTo: leftAnchor),
controlsContainerView.rightAnchor.constraint(equalTo: rightAnchor)
])
controlsContainerView.addSubview(activityIndicatorView)
activityIndicatorView.centerXAnchor.constraint(equalTo: controlsContainerView.centerXAnchor).isActive = true
activityIndicatorView.centerYAnchor.constraint(equalTo: controlsContainerView.centerYAnchor).isActive = true
controlsContainerView.addSubview(pausePlayButton)
pausePlayButton.centerXAnchor.constraint(equalTo: controlsContainerView.centerXAnchor).isActive = true
pausePlayButton.centerYAnchor.constraint(equalTo: controlsContainerView.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 minutsString = String(format: "%02d", Int(seconds/60))
let secondsString = String(format: "%02d", Int(seconds.truncatingRemainder(dividingBy: 60)))
self.currentTimeLabel.text = "\(minutsString):\(secondsString)"
if let duration = self.player?.currentItem?.duration{
let durationSeconds = CMTimeGetSeconds(duration)
self.videoSlider.value = Float(seconds / durationSeconds)
print("videoSlider value \(self.videoSlider.value)")
}
})
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "currentItem.loadedTimeRanges" {
activityIndicatorView.stopAnimating()
controlsContainerView.backgroundColor = .clear
pausePlayButton.isHidden = false
isPlaying = true
if let duration = player?.currentItem?.duration{
let seconds = CMTimeGetSeconds(duration)
let minutsText = String(format: "%02d", Int(seconds)/60)
let secondsText = Int(seconds) % 60
videoLengthLabel.text = "\(minutsText):\(secondsText)"
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer?.frame = bounds
}
}
class VideoVC: UIViewController {
private lazy var playerView: VideoPlayerView = {
let playerView = VideoPlayerView(frame: .zero)
playerView.backgroundColor = .black
return playerView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
view.addSubview(playerView)
playerView.translatesAutoresizingMaskIntoConstraints = false
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let height = view.safeAreaLayoutGuide.layoutFrame.size.width / 1.777777777777778
playerView.frame = CGRect(x: view.safeAreaLayoutGuide.layoutFrame.origin.x, y: view.safeAreaLayoutGuide.layoutFrame.origin.y, width: view.safeAreaLayoutGuide.layoutFrame.size.width, height: height) /* set your desired height here */
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment