Xcode How to detect when a childview within scrollview is opened - swift

I have a viewcontroller with a scrollview inside it. I have three child viewcontrollers linked to this scrollview. In one of the child viewcontrollers, I want to start playing videos with AVPlayer when the user swipes to that view.
Currently with my code, begins the video when the scrollview is loaded even when I am not in that childview.
I have tried calling the download method in viewDidAppear and viewWillAppear.
override func viewDidLoad() {
super.viewDidLoad()
download()
}
func download() {
let fireStoreRef = Firestore.firestore().collection("")
fireStoreRef.getDocuments { (snapshot, err) in
if let snapshot = snapshot{
for document in snapshot.documents{
let stringURL = document.get("videoURL")
if stringURL != nil {
let videoURL = URL(string: stringURL as! String)
self.videoURLs.append(videoURL!)
}
}
self.play(url: self.videoURLs[0])
}
}
}
func play(url: URL) {
let player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
}
Would like the AVplayer to begin playing when user swipes to specific childview of scrollview

Related

Pre-load videos and cache them - swift

I have collectionView and each cell has a video displayed.
When a cell is tapped I present a ViewController that plays the video.
The code that does this in the ViewController is as follows:
fileprivate func configureVideo(with moment : Moment){
guard let urlString = moment.videoUrl else { return }
guard let videoUrl = URL(string: urlString) else { return }
self.player = AVPlayer(url: videoUrl)
dispatchGroup.enter()
self.player!.addObserver(self, forKeyPath: #keyPath(player.currentItem.isPlaybackLikelyToKeepUp),
options: .new, context: &playbackLikelyToKeepUpContext)
NotificationCenter.default.addObserver(self, selector: #selector(videoReachedEnd), name: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)
self.playerLayer = AVPlayerLayer(player: player)
self.view.layer.insertSublayer(playerLayer!, at: 0)
self.playerLayer?.frame = self.view.bounds
self.player?.play()
}
The fact that the video is being loaded when the controller is presented makes the process of video loading longer than I would like it to.
I would like that the videos are being loaded before, when I am scrolling through the cells of the collectionView, so that when I tap on a cell and the controller is presented, the video is already ready to play.
But I don't know what would be the right approach for it.
If you have any suggestions I am willing to listen.

How do I detect when a user unmutes a video in swift when using AVPlayer

I am trying to determine if a user taps on the unmute button while watching a video. I am aware of player.isMuted but I am not sure how to check if there is a change if value. I am using AVPlayerVideoViewController and want to override the unmute button functionality.
I created a sample ViewController, you can observe isMuted changes easily:
import AVKit
class ViewController: AVPlayerViewController {
var muteObservation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
guard let videoPath = Bundle.main.path(forResource: "video", ofType: "mov") else {
return
}
let videoURL = URL(fileURLWithPath: videoPath)
player = AVPlayer(url: videoURL)
player?.play()
muteObservation = player?.observe(\.isMuted) { player, _ in
print("isMuted: \(player.isMuted)")
}
}
}

Call Controller of an autoplayed video in ViewController

I have set it up so that when a ViewController is displayed, a video starts automatically and at the end it switches to a different ViewController.
The problem is that if the app is put in the background while viewing it, the video freezes and you have to restart the application.
I thought about setting the classic pause / play controllers to appear when you press the screen so you can continue watching, but I don't know how to do that.
Or do you have another solution to prevent the video from freezing?
import UIKit
import AVKit
import AVFoundation
class View8BaController: UIViewController {
func setupAVPlayer() {
let videoURL = Bundle.main.url(forResource: "8B-A", withExtension: "mp4") // Get video url
let avAssets = AVAsset(url: videoURL!) // Create assets to get duration of video.
let avPlayer = AVPlayer(url: videoURL!) // Create avPlayer instance
let avPlayerLayer = AVPlayerLayer(player: avPlayer) // Create avPlayerLayer instance
avPlayerLayer.frame = self.view.bounds // Set bounds of avPlayerLayer
self.view.layer.addSublayer(avPlayerLayer) // Add avPlayerLayer to view's layer.
avPlayer.play() // Play video
// Add observer for every second to check video completed or not,
// If video play is completed then redirect to desire view controller.
avPlayer.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 1) , queue: .main) { [weak self] time in
if time == avAssets.duration {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SCENA7") as! SCENA7ViewController
self?.navigationController?.pushViewController(vc, animated: false)
}
}
}
//------------------------------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
}
//------------------------------------------------------------------------------
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.setupAVPlayer() // Call method to setup AVPlayer & AVPlayerLayer to play video
}
}
have you tried telling the video to play in the sceneDelegate?
add to the class, right above func setupAVPlayer(),
var avPlayer: AvPlayer!
then in your sceneDelegate outside any functions
let view8Ba = View8BaController()
to create an instance of the view controller. Then you can access the properties in the following:
func sceneWillEnterForeground(_ scene: UIScene) {
if view8Ba.viewIfLoaded?.window != nil {
view8Ba.avPlayer.play()
}
}
This will tell your video to start playing again when the app comes back from the background.
If you want to add a play/pause when you tap the screen you can add a tap gesture recognizer and another view to the current view controller and set the background to clear (in storyboard drag the new view to the white bar on top of the view controller)
then call
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
pauseScreenView.frame = View8BaController.bounds
}
This tells the new view you added where to be on the viewController.
In the IBAction of the tap gesture recognizer
#IBAction func screenTapped(_ sender: Any) {
View8BaController.addSubview(pauseScreenView)
DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
self.pauseScreenView.removeFromSuperview()
}
}
This adds the new view to the top of the viewController and then removes it after 10 seconds.
In that new view you can add a button that will play/pause your video
#IBAction func pauseVideo(_ sender: UIButton) {
if avPlayer.timeControlStatus == .playing {
avPlayer.pause()
pauseButton.setImage(playImage, for: .normal)
}else {
avPlayer.play()
pauseButton.setImage(pauseImage, for: .normal)
}
}

How to detect when AVPlayer starts/stops playing in swift

I have an AVPlayer inside a scrollView and for some reason when the AVPlayer plays the scrollview jumps to the top causing the video to not be visible on the screen as the video is torward the bottom of the scrollView. So inorder to stop the scrollView from jumping I want to disable and enable scrolling when the user starts/stops the video. Not sure if that will solve my problem but I'm hoping someone knows the right way to add an observer or notification for the player so I can test it out.
This is the code I have inside my ViewController that contains some static text and the AVPlayer.
Code Below:
let path = NSBundle.mainBundle().pathForResource("carbon_video", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
var player = AVPlayerViewController()
var avPlayer = AVPlayer(URL: url)
player.player = avPlayer
self.addChildViewController(player)
player.view.translatesAutoresizingMaskIntoConstraints = false
player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.New, context: nil)
player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.New, context: nil)
I think this can help you
func playerItemDidReachEnd(notification: NSNotification) {
let p: AVPlayerItem = notification.object as! AVPlayerItem
p.seekToTime(kCMTimeZero)
}
override func viewDidAppear(animated: Bool) {
avPlayer.play()
paused = false
// Disable scrolling
}
override func viewDidDisappear(animated: Bool) {
avPlayer.pause()
paused = true
// Enable scrolling
}

AVPlayer will not stop playing when going back to the previous view controller (SWIFT)

I have two View Controllers a TableViewController where i have a list of musics and a UIViewController where it displays the music details and plays the music. The music automatically plays when the view is loaded and pauses when the pause button is pressed. However whenever I go back to the previous TableViewController to select another music, the music continues to play. And if i select another music, both of them are playing together
override func viewDidLoad() {
super.viewDidLoad()
timeLabel.text = "00:00"
if let object = currentObject {
audioTitle.text = object["audioTitle"] as? String
let days = object["daysActive"] as! Int
daysActive.text = "Powertalks: Day \(days)"
var initialThumbnail = UIImage(named: "trc_app_icon.png")
audioImage.image = initialThumbnail
if let thumbnail = object["image"] as? PFFile {
audioImage.file = thumbnail
audioImage.loadInBackground()
}
if let audioFile = object["audioFile"] as? PFFile {
if let audioPath: String = audioFile.url {
audioPlayer = AVPlayer(URL: NSURL(string: audioPath))
audioSlider.minimumValue = 0
audioSlider.maximumValue = Float(CMTimeGetSeconds(audioPlayer.currentItem.asset.duration))
audioSlider.value = Float(CMTimeGetSeconds(audioPlayer.currentTime()))
audioPlayer.volume = volumeSlider.value
playAudio()
}
}
}
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateSlider"), userInfo: nil, repeats: true )
}
You have to pause the player when the view disappears. Although AVPlayer doesn't have a stop method, you can set the rate to 0.0 (or use pause()) and set currentItem to nil to achieve the same effect. Try using the below code (not tested)
override func viewWillDisappear(animated: Bool) {
audioPlayer.pause()
audioPlayer.currentItem = nil
}
I tested the code below which works fine:
override func viewWillDisappear(animated: Bool) {
audioPlayer.pause()
audioPlayer.currentItem = nil
}
You could also use:
audioPlayer.stop()