Call Controller of an autoplayed video in ViewController - swift

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

Related

Returning to a previous view controller

I have a view controller which I made through coding rather than storyboard to display a video.
once the video has finished playing I cannot get the view controller to return to the previous one. I have a button which takes the user to the video but when the video finish it doesn't go back to the previous view controller.
import AVKit
import AVFoundation
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "tour", ofType: "mov")!))
let vc = AVPlayerViewController()
vc.player = player
present(vc, animated: true)
}
}
this is my code for my video. I have done everything else to build the app on my storyboard
To go back to the previous VC after the video finishes, you need to -
add an observer which could detect when the video stops playing and then -
dismiss the AVPlayerViewController
To accomplish task 1 - this answer by Channel helps set up the observer -https://stackoverflow.com/a/40056529/13451699
To do task 2 - just dismiss the AVPlayerViewController from inside the playerDidFinishPlaying function, like so :
func playerDidFinishPlaying(note: NSNotification){
// Video finished playing
self.dismiss(animated: true, completion: nil)
}

How can I tap to close AVPlayer in Swift 5?

I'm creating an extremely simple game, and I've hidden the video controls.
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
One of the two options would be OK.
Option 1: Tap to close the video.
Option 2: Have the AVPlayer close automatically at the end of the video.
I appreciate any help.
Thanks!
You can add a tap gesture recognizer to AVPlayerViewController's view (for closing on tap), or you could subscribe to AVPlayerItemDidPlayToEndTime notification (for closing when video ends playing). Something like this:
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
//for closing when video ends
NotificationCenter.default.addObserver(self, selector: #selector(closePlayer), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: controller.player?.currentItem)
//for closing on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayer))
controller.view.addGestureRecognizer(tapGestureRecognizer)
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
#objc func closePlayer() {
dismiss(animated: true)
//if you go notification route, don't forget to remove observer
NotificationCenter.default.removeObserver(self)
}
For closing the videoPlayer when on user tap you can use the following-
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayerOnTouch))
controller.view.addGestureRecognizer(tapGestureRecognizer)
and then add this after the button -
#objc func closePlayerOnTouch() {
dismiss(animated: true)
NotificationCenter.default.removeObserver(self)
}
#Predrag's answer is really awesome though.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var btnPlay: UIButton!
var player:AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPress(sender: AnyObject) {
if (btnPlay.titleLabel?.text == "Play") {
initPlayer()
btnPlay.setTitle("Stop", forState: UIControlState.Normal)
} else {
stopPlayer()
btnPlay.setTitle("Play", forState: UIControlState.Normal)
}
}
func initPlayer() {
if let play = player {
print("playing")
play.play()
} else {
print("player allocated")
player = AVPlayer(URL: NSURL(string: "http://streaming.radio.rtl.fr/rtl-1-48-192")!)
print("playing")
player!.play()
}
}
func stopPlayer() {
if let play = player {
print("stopped")
play.pause()
player = nil
print("player deallocated")
} else {
print("player was already deallocated")
}
}
}

How can I show same video as a background in all View Controllers without any interruption during the pages transition?

There are 2 pages(ViewController) in Main.storyboard: HomeViewController and DetailViewController
I gave a storyboard ID for the DetailViewController:DetailPage
I have added a button into first page to take user to the second page.
I also have added a back button into second page to take user to the first page back.
I need to show a video in the background in all pages.
So I have added an UIView component in all pages to show a video.
I gave 0,0,0,0 constraint values for these 2 UIView components in each pages.
First let me share source codes and then I would like to ask my questions.
HomeViewController.swift file
import UIKit
import AVFoundation
class HomeViewController: UIViewController {
private var player: AVPlayer!
#IBOutlet weak var outlet4TheVideoUiView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
configureVideoIssue()
}
override func viewWillAppear(_ animated: Bool) {
configureVideoIssue()
}
func configureVideoIssue()
{
// BACKGROUND VIDEO SCOPE STARTS
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "clouds", ofType: "mp4")!)
let player = AVPlayer(url: path)
self.player = player
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.view.bounds
outlet4TheVideoUiView.frame = self.view.bounds
outlet4TheVideoUiView.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
// video bitince tekrar oynatmak için
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidPlayToEnd(notification:)),
name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
// BACKGROUND VIDEO SCOPE ENDS
}
#objc func enteredBackground() {
player.pause()
}
#objc func enteredForeground() {
player.play()
}
#objc func videoDidPlayToEnd(notification: Notification)
{
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: .zero, completionHandler: nil)
}
#IBAction func linkBtnClick(_ sender: UIButton) {
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DetailPage") as! DetailViewController
controller.modalPresentationStyle = .fullScreen
//present(controller, animated: true, completion: nil)
show(controller, sender: nil)
}
}
DetailViewController.swift file
import UIKit
import AVFoundation
class DetailViewController: UIViewController {
private var player: AVPlayer!
#IBOutlet weak var outlet4TheVideoUiView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
configureVideoIssue()
// Do any additional setup after loading the view.
}
func configureVideoIssue()
{
// BACKGROUND VIDEO SCOPE STARTS
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "clouds", ofType: "mp4")!)
let player = AVPlayer(url: path)
self.player = player
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.view.bounds
outlet4TheVideoUiView.frame = self.view.bounds
outlet4TheVideoUiView.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
// video bitince tekrar oynatmak için
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidPlayToEnd(notification:)),
name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
// BACKGROUND VIDEO SCOPE ENDS
}
#objc func enteredBackground() {
player.pause()
}
#objc func enteredForeground() {
player.play()
}
#objc func videoDidPlayToEnd(notification: Notification)
{
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: .zero, completionHandler: nil)
}
#IBAction func btnBackClick(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
First Question: I am sure that it shouldn't be like this because same codes are written in both ViewControllers. So how can I avoid this? Any suggestions? I should use same video ui view object as a background for all view controllers. Does it make sense? If it does not make sense. What should I do? For example, I use asp.net user control component in Visual Studio for this scenario. I create one video component(user control) and I can use this component for all pages as a background.
Second Question: (if we can't find a solution to the first question)Let's assume that app user sees Homepage right now. And let's assume that user clicks button to see secondpage(detail page)after 10th second. Button click action takes user to the second page but video starts from the first second. Video should continue from 10th second or 11th second. How can I do this?
Third Question: Second page comes from the bottom after i click button in first page. Can second page comes with fading first page out animation and appearing second page animation?
Important details: Video should stop after app is minimized and should continue after app is maximized by app user. And other important detail is: buttons are not inside of the video view. User can see buttons above the video with this way. Let me show layers for the components in order with an image:
One way that you can do this is set your view on the rootWindow and then set all your views' background color to clear.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let window=self.window!
backgroundVideoView.bounds = window.bounds
window.addSubview(backgroundVideoView)
playVideo()
return true
}
Here, backgroundVideoView refers to the view which will be playing your video.
Although, I must warn you, this will consume a lot of memory but will get you the desired behavior.

AVPlayer not stop playing when going back to the previous view

I have two View Controllers a TableViewController where I have a list of musics and a UIViewController where it 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.
My Codes:
var audioplayer = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
play()
}
func play(){
var songUrl = songList[selectedItem].url
audioplayer = AVPlayer(url: songUrl!)
audioplayer.play()
}
Have you tried to use .pause() ? example:
var audioplayer: AVPlayer? = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
play()
}
func play(){
var songUrl = songList[selectedItem].url
audioplayer = AVPlayer(url: songUrl!)
audioplayer.play()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
audioplayer.pause()
audioplayer = nil
}
I already fixed my problem. I remove all player items in view controller and create new class for player. Now ıt ıs workıng smoothly.

Stop video playback on Siri Remote Menu Button press

In my application I do play a video with that function
func easyRandom_play(episodeTitle:String){
self.backgroundMusic?.stop()
backgroundMusic?.stop()
let link = "http://46.235.26.87/\(__TITLENAME_FOLDERNAME_DOWNLOAD__)/\(episodeTitle).m4v"
print(link)
let url:NSURL = NSURL(string: link)!
let player = AVPlayer(URL: url)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.presentViewController(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
Now there is no way to stop the video - normally the menu button should take me back and end the playback.
Is there a way to implement this?
You can add a UITapGestureRecognizer to handle the Menu button.
override func viewDidLoad() {
// Setup Menu Button recognizer
let menuGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleMenuGesture(_:)))
menuGesture.allowedPressTypes = [NSNumber(integer: UIPressType.Menu.rawValue)]
self.view.addGestureRecognizer(menuGesture)
}
func handleMenuGesture(tap: UITapGestureRecognizer) {
print("Menu Gesture")
// Stop video and dismiss view controller here
}