AVPlayerViewController not deallocating immediately - ios

I am playing a video in my app in TVos. I am using AVPlayerViewController to play the video. But when i press Menu button on Apple Tv remote i goes back to the view controller from which i came here but video continues to play and after 8 or 10 second it gets deallocated. This is really a bad bug and i am stuck on this for few days. Any help will be highly appreciated.
Here is my code for the view controller.
import Foundation
import UIKit
import AVKit
class ViewController : UIViewController {
var avplayerVC : AVPlayerViewController?
var recentlyWatchedTimer : NSTimer?
var lessonToWatch : Lesson?
override func viewDidLoad() {
super.viewDidLoad()
if let urlVideo = lessonToWatch?.lessonurl {
let activityIndicator = UIActivityIndicatorView(frame: CGRectMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0, 30.0, 30.0))
let asset : AVURLAsset = AVURLAsset(URL: NSURL.init(string: urlVideo)!, options: nil)
let keys = ["playable"];
avplayerVC = AVPlayerViewController()
weak var weakSelf = self
asset.loadValuesAsynchronouslyForKeys(keys) { () -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
weakSelf!.avplayerVC?.player = AVPlayer(playerItem: AVPlayerItem(asset: asset))
weakSelf!.avplayerVC?.player?.seekToTime(kCMTimeZero)
print("Status 1: " + "\(self.avplayerVC?.player?.status.rawValue)")
print(self.view?.frame)
// doesn't work
weakSelf!.avplayerVC?.view.frame = self.view.frame
activityIndicator.stopAnimating()
activityIndicator.removeFromSuperview()
weakSelf!.view.addSubview((self.avplayerVC?.view!)!)
weakSelf!.avplayerVC?.player?.play()
weakSelf!.recentlyWatchedTimer = NSTimer.scheduledTimerWithTimeInterval(20.0, target: self, selector: "addToRecentlyWatched" , userInfo: nil, repeats: false)
})
}
print("In LessonPlayViewController View Did Load")
self.view.addSubview(activityIndicator)
activityIndicator.startAnimating()
}
}
func addToRecentlyWatched() {
if let lesson = lessonToWatch {
DataManager.sharedInstance.addRecentlyWatch(lesson)
}
recentlyWatchedTimer?.invalidate()
}
deinit {
print("deinit")
avplayerVC?.view.removeFromSuperview()
avplayerVC?.player = nil
avplayerVC = nil
}
// MARK : AVPlayerViewControllerDelegate
}

You want to use weakSelf in all places inside the async callback block, but you're printing self.view.frame at once place.

You should use a capture list to make sure all references to self are weak within the closure. Then use a guard to check that the references are still valid within the closure.
import Foundation
import UIKit
import AVKit
import AVFoundation
class ViewController : UIViewController {
var avplayerVC : AVPlayerViewController?
var recentlyWatchedTimer : NSTimer?
var lessonToWatch : Lesson?
override func viewDidLoad() {
super.viewDidLoad()
if let urlVideo = lessonToWatch?.lessonurl {
let activityIndicator = UIActivityIndicatorView(frame: CGRectMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0, 30.0, 30.0))
let asset : AVURLAsset = AVURLAsset(URL: NSURL.init(string: urlVideo)!, options: nil)
let keys = ["playable"];
avplayerVC = AVPlayerViewController()
asset.loadValuesAsynchronouslyForKeys(keys) { [weak self] in
dispatch_async(dispatch_get_main_queue()) { [weak self] in
guard let vc = self, playerVC = vc.avplayerVC else {
return
}
playerVC.player = AVPlayer(playerItem: AVPlayerItem(asset: asset))
playerVC.player?.seekToTime(kCMTimeZero)
print("Status 1: " + "\(playerVC.player?.status.rawValue)")
print(vc.view?.frame)
// doesn't work
playerVC.view.frame = vc.view.frame
activityIndicator.stopAnimating()
activityIndicator.removeFromSuperview()
vc.view.addSubview(playerVC.view)
playerVC.player?.play()
vc.recentlyWatchedTimer = NSTimer.scheduledTimerWithTimeInterval(20.0, target: vc, selector: "addToRecentlyWatched" , userInfo: nil, repeats: false)
}
}
print("In LessonPlayViewController View Did Load")
self.view.addSubview(activityIndicator)
activityIndicator.startAnimating()
}
}
func addToRecentlyWatched() {
if let lesson = lessonToWatch {
DataManager.sharedInstance.addRecentlyWatch(lesson)
}
recentlyWatchedTimer?.invalidate()
}
deinit {
print("deinit")
avplayerVC?.view.removeFromSuperview()
avplayerVC?.player = nil
avplayerVC = nil
}
// MARK : AVPlayerViewControllerDelegate
}
More on capture lists:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID57
Edit
Also note that you should explicitly stop the video playback when leaving the view controller and not rely on the player to stop the playback as part of its deallocation.
I.e. in viewDidDisappear or alike.

Related

Swift 4 & Xcode 10. Play video on app launch, when complete, reveal view controller

Firstly, I'm totally new to Xcode 10 and Swift 4, and I've searched here but haven't found code that works.
What I'm after:
On launching app to play a video which is stored locally (called "launchvideo").
On completion of video to display/move to a UIviewcontroller with a storyboard ID of "menu"
So far I have my main navigation controller with it's linked view controller.
I'm guessing I need a UIview to hold the video to be played in on this page?
Is there someone who can help a new guy out?
Thanks
Firstly change your launch screen storyboard to Main storyboard from project settings in General tab.
Create one view controller with following name and write code to implement AVPlayer to play video.
import UIKit
import AVFoundation
class VideoLaunchVC: UIViewController {
func setupAVPlayer() {
let videoURL = Bundle.main.url(forResource: "Video", withExtension: "mov") // 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: "ViewController") as! ViewController
self?.navigationController?.pushViewController(vc, animated: true)
}
}
}
//------------------------------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
}
//------------------------------------------------------------------------------
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.setupAVPlayer() // Call method to setup AVPlayer & AVPlayerLayer to play video
}
}
Main.Storyboard:
Project Launch Screen File:
See following video also:
https://youtu.be/dvi0JKEpNTc
You have to load a video on launchvideoVC, like below way in swift 4 and above
import AVFoundation
import AVKit
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
initVideo()
}
func initVideo(){
do {
try AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print(error)
}
let path = Bundle.main.path(forResource: "yourlocalvideo", ofType:"mp4");
player = AVPlayer(url: NSURL(fileURLWithPath: path!) as URL)
NotificationCenter.default.addObserver(self, selector: #selector(launchvideoVC.itemDidFinishPlaying(_:)), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
DispatchQueue.main.async(execute: {() -> Void in
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.view.bounds
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = 1
self.view.layer.addSublayer(playerLayer)
self.player?.seek(to: CMTime.zero)
self.player?.play()
})
}
#objc func itemDidFinishPlaying(_ notification: Notification?) {
//move to whatever UIViewcontroller with a storyboard ID of "menu"
}
I think it's may help you.
Happy coding :)
First you make a new view controller with view and change your launch screen storyboard to Main storyboard from project settings in General tab.
And also add your video in folder.
Then just add my below code to your launch screenView controller:
import UIKit
import MediaPlayer
import AVKit
class LaunchViewController: UIViewController {
fileprivate var rootViewController: UIViewController? = nil
var player: AVPlayer?
var playerController = AVPlayerViewController()
override func viewDidLoad() {
super.viewDidLoad()
showSplashViewController()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func playVideo() {
let videoURL = NSURL(string: "videoplayback")
player = AVPlayer(url: videoURL! as URL)
let playerController = AVPlayerViewController()
playerController.player = player
self.addChildViewController(playerController)
// Add your view Frame
playerController.view.frame = self.view.frame
// Add subview in your view
self.view.addSubview(playerController.view)
player?.play()
}
private func loadVideo() {
//this line is important to prevent background music stop
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
} catch { }
let path = Bundle.main.path(forResource: "videoplayback", ofType:"mp4")
let filePathURL = NSURL.fileURL(withPath: path!)
let player = AVPlayer(url: filePathURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.frame
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = -1
self.view.layer.addSublayer(playerLayer)
player.seek(to: kCMTimeZero)
player.play()
}
func showSplashViewControllerNoPing() {
if rootViewController is LaunchViewController {
return
}
loadVideo()
}
/// Simulates an API handshake success and transitions to MapViewController
func showSplashViewController() {
showSplashViewControllerNoPing()
delay(6.00) {
self.showMenuNavigationViewController()
}
}
public func delay(_ delay:Double, closure:#escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
/// Displays the MapViewController
func showMenuNavigationViewController() {
guard !(rootViewController is Home) else { return }
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nav = storyboard.instantiateViewController(withIdentifier: "homeTab") as! Home
nav.willMove(toParentViewController: self)
addChildViewController(nav)
if let rootViewController = self.rootViewController {
self.rootViewController = nav
rootViewController.willMove(toParentViewController: nil)
transition(from: rootViewController, to: nav, duration: 0.55, options: [.transitionCrossDissolve, .curveEaseOut], animations: { () -> Void in
}, completion: { _ in
nav.didMove(toParentViewController: self)
rootViewController.removeFromParentViewController()
rootViewController.didMove(toParentViewController: nil)
})
} else {
rootViewController = nav
view.addSubview(nav.view)
nav.didMove(toParentViewController: self)
}
}
override var prefersStatusBarHidden : Bool {
switch rootViewController {
case is LaunchViewController:
return true
case is Home:
return false
default:
return false
}
}
}

AVPlayer keeps adding streams instead of replacing

I am triggering a video play from a URL. It's just a button, opening the AVPlayerController full screen. If people close it, the can go back to the another item, with possibly a video. There they can click that video to start, however, when they do so, I can hear the audio of the previous player, of the other VC playing in the together with this one. This keeps layering up. How can I avoid this?
This is my class for the videoplayer
import UIKit
import AVFoundation
import AVKit
class simpleVideoPlayer: UIViewController {
var playerController = AVPlayerViewController()
var player:AVPlayer?
var inputVideoUrl: String? = nil
func setupVideo() {
self.player = AVPlayer()
self.playerController.player = self.player
}
func playNext(url: URL) {
let playerItem = AVPlayerItem.init(url: url)
self.playerController.player?.replaceCurrentItem(with: playerItem)
self.playerController.player?.play()
}
func setupVideoUrl(url: String) {
inputVideoUrl = url
}
}
This is in my viewcontroller. It's first getting a URL of a possible advert from my server, if that failed, then it wil just load the "default" video.
let SimpleVideo = simpleVideoPlayer()
#objc func handleTap(gestureRecognizer: UIGestureRecognizer)
{
ApiVideoAdvertService.sharedInstance.fetchVideoAdvert { (completion: VideoAdvert) in
let advertUrl = URL(string: completion.video_adverts_url)
var url = URL(string: (self.article?.video_link?.files[0].link_secure)!)
var showAdvert: Bool = false
if (advertUrl != nil && UIApplication.shared.canOpenURL(advertUrl!)) {
url = advertUrl
showAdvert = true
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
if (showAdvert) {
NotificationCenter.default.addObserver(self, selector: #selector(self.finishVideo),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.SimpleVideo.playerController.player?.currentItem)
}
appDelegate.window?.rootViewController?.present(self.SimpleVideo.playerController, animated: true, completion: {
self.SimpleVideo.setupVideo()
if (showAdvert) {
self.SimpleVideo.playerController.setValue(true, forKey: "requiresLinearPlayback")
}
self.SimpleVideo.playNext(url: url!)
})
}
#objc func finishVideo() {
let url = URL(string: (article?.video_link?.files[0].link_secure)!)
SimpleVideo.playerController.setValue(false, forKey: "requiresLinearPlayback")
SimpleVideo.playNext(url: url!)
}
Removing the observer inside finishVideo did it.
#objc func finishVideo() {
NotificationCenter.default.removeObserver(self)
let url = URL(string: (article?.video_link?.files[0].link_secure)!)
SimpleVideo.playerController.setValue(false, forKey: "requiresLinearPlayback")
SimpleVideo.playNext(url: url!)
}

AVPlayer video still playing after segue to next view controller

I'm currently looping my mp4 video with no playback controls, kind of like a gif but with sound. But I do not know why when I segue to the next view controller, the video is still playing. Does anybody know the simplest method to resolve this issue?
ViewController
import UIKit
import AVKit
import AVFoundation
fileprivate var playerObserver: Any?
class ScoreController: UIViewController {
#IBOutlet var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let returnValue: Int = UserDefaults.standard.integer(forKey: "userScore")
label.text = "Your Score: \(returnValue)/30"
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
let path = Bundle.main.path(forResource: "Innie-Kiss", ofType:"mp4")
let player = AVPlayer(url: URL(fileURLWithPath: path!))
let resetPlayer = {
player.seek(to: kCMTimeZero)
player.play()
}
playerObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: nil) { notification in resetPlayer() }
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
self.addChildViewController(controller)
let screenSize = UIScreen.main.bounds.size
let videoFrame = CGRect(x: 0, y: 130, width: screenSize.width, height: (screenSize.height - 130) / 2)
controller.view.frame = videoFrame
self.view.addSubview(controller.view)
player.play()
} catch {
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
}
fileprivate var player: AVPlayer? {
didSet { player?.play() }
}
deinit {
guard let observer = playerObserver else { return }
NotificationCenter.default.removeObserver(observer)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func doneButton(_ sender: Any) {
self.performSegue(withIdentifier: "done", sender: self)
}
}
In viewWillDisapper() or button action for segue do this :
NotificationCenter.default.removeObserver(self)
Also move this from viewDidLoad() to some function like :
var player: AVPlayer?
func audioPlayer(){
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
let path = Bundle.main.path(forResource: "Innie-Kiss", ofType:"mp4")
player = AVPlayer(url: URL(fileURLWithPath: path!))
let resetPlayer = {
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
playerObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: nil) { notification in resetPlayer() }
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
self.addChildViewController(controller)
let screenSize = UIScreen.main.bounds.size
let videoFrame = CGRect(x: 0, y: 130, width: screenSize.width, height: (screenSize.height - 130) / 2)
controller.view.frame = videoFrame
self.view.addSubview(controller.view)
player?.play()
} catch {
}
}
and make player object a global variable. var player = AVPlayer? and in viewWillDisappear make it nil.
So your viewWillDisappear should look like this :
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self)
if player != nil{
player?.replaceCurrentItem(with: nil)
player = nil
}
}
override func viewWillDisappear(_ animated: Bool) {
}
This may be helpful to you if you are switching windows; as you perform a segue, you will execute the function included in curly brackets automatically. I'm rubbish with AV, but some of the other's code suggested to turn off the function may work here if it isn't working in the IBAction.
I'm not great with Xcode though, so mileage will probably vary.
I had this issue and fixed it by adding this line before setting player to nil:
[self.player pause];
here is my viewWillDisapear:
- (void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.player];
[self.player removeObserver:self forKeyPath:#"rate"];
[self.player removeTimeObserver:self.playerObserver];
self.playerObserver = nil;
[self.player pause];
self.player = nil;
[[UIDevice currentDevice] setValue:#(UIInterfaceOrientationPortrait) forKey:#"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];
[StayfilmApp restrictOrientation:YES];
}
class ScoreController: UIViewController {
#IBOutlet var label: UILabel!
var player: AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
let returnValue: Int = UserDefaults.standard.integer(forKey: "userScore")
label.text = "Your Score: \(returnValue)/30"
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
let path = Bundle.main.path(forResource: "Innie-Kiss", ofType:"mp4")
player = AVPlayer(url: URL(fileURLWithPath: path!))
let resetPlayer = {
player.seek(to: kCMTimeZero)
player.play()
}
playerObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: nil) { notification in resetPlayer() }
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
self.addChildViewController(controller)
let screenSize = UIScreen.main.bounds.size
let videoFrame = CGRect(x: 0, y: 130, width: screenSize.width, height: (screenSize.height - 130) / 2)
controller.view.frame = videoFrame
self.view.addSubview(controller.view)
player.play()
} catch {
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player.pause()
}
fileprivate var player: AVPlayer? {
didSet { player?.play() }
}
deinit {
guard let observer = playerObserver else { return }
NotificationCenter.default.removeObserver(observer)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func doneButton(_ sender: Any) {
self.performSegue(withIdentifier: "done", sender: self)
}
}
notable changes:
var player: AVPlayer?
and also
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
player?.pause()
}
I have the same problem when I used segue to go to the next ViewController, then I used present code with swift by using this code
#IBAction func next(_ sender: Any) {
let vc = self .storyboard?.instantiateViewController(withIdentifier:"ViewControllerAA") as! ViewControllerAA
self.present(vc,animated: true,completion: nil)
}
but the problem didn't solve , then I used this code to go to the next VC and my problem solved and the background sound of the video stoped, and the code is this below
#IBAction func next(_ sender: Any) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerAA") as? ViewControllerAA {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = vc
}
}

How to play video in ios swift

I am new to ios programming, actually I need to stream video URL but i am unable to stream video URL using avplayer, but using avplayer downloaded file i am able to play.Actual problem is my file format is different
for example my file name is like this song.apa but it is song.mp4
code:
let avplayerController = AVPlayerViewController()
var avPlayer:AVPlayer?
override func viewDidLoad()
{
super.viewDidLoad()
let movieUrl = URL.init(string: "http://techslides.com/demos/sample-videos/small.3gp")
self.avPlayer = AVPlayer.init(url: movieUrl!)
self.avplayerController.player = self.avPlayer
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playVideo(_ sender: Any)
{
self.present(self.avplayerController, animated: true) {
self.avplayerController.player?.play()
}
}
In Swift 3
import Foundation
import AVFoundation
import MediaPlayer
import AVKit
func play()
{
let player = AVPlayer(url: "Your Url")
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true)
{
playerViewController.player!.play()
}
}
You cant play this particular link (http://techslides.com/demos/sample-videos/small.3gp) using AVPlayer or AVPlayerViewController because that file is in AMR (Adaptive Multi-Rate, a format for speech) - which is not supported since iOS 4.3
Some of 3gp files can be played but only ones that have video and audio streams supported by Apple.
In Swift 3, try this code for playing video in project
import UIKit
import AVKit
import AVFoundation
import MediaPlayer
import MobileCoreServices
class VideoPlayerViewController: UIViewController,AVPlayerViewControllerDelegate {
//MARK: - Outlet -
#IBOutlet weak var viewVidioPlayer: UIView!
//MARK: - Variable
//MARK: - View Life Cycle -
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - Action -
//for Playing Video
#IBAction func btnvideoPlayClicked(_ sender: UIButton) {
self.videoPlay()
}
func videoPlay()
{
let playerController = AVPlayerViewController()
playerController.delegate = self
let bundle = Bundle.main
let moviePath: String? = "http://techslides.com/demos/sample-videos/small.3gp"
let movieURL = URL(fileURLWithPath: moviePath!)
let player = AVPlayer(url: movieURL)
playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
player.play()
}
//MARK: - other Function -
func playerViewControllerWillStartPictureInPicture(_ playerViewController: AVPlayerViewController){
print("playerViewControllerWillStartPictureInPicture")
}
func playerViewControllerDidStartPictureInPicture(_ playerViewController: AVPlayerViewController)
{
print("playerViewControllerDidStartPictureInPicture")
}
func playerViewController(_ playerViewController: AVPlayerViewController, failedToStartPictureInPictureWithError error: Error)
{
print("failedToStartPictureInPictureWithError")
}
func playerViewControllerWillStopPictureInPicture(_ playerViewController: AVPlayerViewController)
{
print("playerViewControllerWillStopPictureInPicture")
}
func playerViewControllerDidStopPictureInPicture(_ playerViewController: AVPlayerViewController)
{
print("playerViewControllerDidStopPictureInPicture")
}
func playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart(_ playerViewController: AVPlayerViewController) -> Bool
{
print("playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart")
return true
}
}
I hope it's work for you,
thank you
heres an example of streaming video..
override func viewDidLoad() {
super.viewDidLoad()
let player = AVPlayer(url: URL(string: "http://techslides.com/demos/sample-videos/small.mp4")!)
let controller = AVPlayerViewController()
present(controller, animated: true) { _ in }
controller.player = player
addChildViewController(controller)
view.addSubview(controller.view)
controller.view.frame = CGRect(x: 50, y: 50, width: 300, height: 300)
controller.player = player
controller.showsPlaybackControls = true
player.isClosedCaptionDisplayEnabled = false
player.play()
}
you can use YoutubePlayerView for playing youtube videos, and for else formats you can use UIWebView. For doing so, simply use if-else block,
if movieUrl.contains("youtube.com/") {
var videoId: String = extractYoutubeId(fromLink: movieUrl)
youtubePlayerView.load(withVideoId: videoId)
} else {
webView = UIWebView(frame: CGRect(x: 0, y: 0, width: 288, height: 277))
webView.delegate = self
webView.backgroundColor = UIColor.black
webView?.loadRequest(movieUrl)
}
For Swift 3 play video from URL
//AVPlayerViewControllerDelegate // example : class TestAudioVideoVC: UIViewController,AVPlayerViewControllerDelegate
func videoPlay()
{
let playerController = AVPlayerViewController()
playerController.delegate = self
let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL! as URL)
playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = CGRect(x: 0, y: 64, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - 64)
playerController.showsPlaybackControls = true
player.play()
}
//MARK: - AVPlayerViewController Delegate -
func playerViewControllerWillStartPictureInPicture(_ playerViewController: AVPlayerViewController){
print("playerViewControllerWillStartPictureInPicture")
}
func playerViewControllerDidStartPictureInPicture(_ playerViewController: AVPlayerViewController)
{
print("playerViewControllerDidStartPictureInPicture")
}
func playerViewController(_ playerViewController: AVPlayerViewController, failedToStartPictureInPictureWithError error: Error)
{
print("failedToStartPictureInPictureWithError")
}
func playerViewControllerWillStopPictureInPicture(_ playerViewController: AVPlayerViewController)
{
print("playerViewControllerWillStopPictureInPicture")
}
func playerViewControllerDidStopPictureInPicture(_ playerViewController: AVPlayerViewController)
{
print("playerViewControllerDidStopPictureInPicture")
}
func playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart(_ playerViewController: AVPlayerViewController) -> Bool
{
print("playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart")
return true
}
Play video in swift 5
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func playVideoUsingURL(_ sender: Any) {
playGlobalVideo()
}
#IBAction func playVideoFromLocalDrive(_ sender: Any) {
playLocalVideo()
}
func playGlobalVideo() {
guard let videoURL = URL(string: "http://static.videokart.ir/clip/100/480.mp4") else {
return
}
let player = AVPlayer(url: videoURL)
let vc = AVPlayerViewController()
vc.player = player
present(vc, animated: true) {
player.play()
}
}
func playLocalVideo() {
guard let videoPath = Bundle.main.path(forResource: "movie", ofType: "mov") else {
return
}
let player = AVPlayer(url: URL.init(fileURLWithPath: videoPath))
let playCtr = AVPlayerViewController()
playCtr.player = player
present(playCtr, animated: true) {
player.play()
}
}
}

warning: attempt to present whose view is not in the window hierarchy

I have a UITabView in UIViewController, all tab items are linked to other UIViewControllers. I have written a swift code of downloading a file through internet. when I select second tabItem, this code runs well, it downloads and previews the downloaded file, Then when I click on first tabItem and then again click on second tabItem; file downloads well but it doesn't show any preview instead xCode gives me a warning message:
What I want is download file and preview file both should work when I again click on the second tabItem. whatever the code is.
warning: attempt to present QLPreviewController on KPIViewController whose view is not in the window hierarchy
I have found many solutions on the internet but it didn't work
first solution says to use
let viewer = UIDocumentInteractionController(URL: NSURL(fileURLWithPath: path))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(viewer, animated: true, completion: nil)
but this function
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(viewer, animated: true, completion: nil)
do not accept
UIDocumentInteractionController
second solution says to override the existing presentViewController function to
override func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
let APP_DELEGATE = UIApplication.sharedApplication().delegate
var presentedModalVC:UIViewController = (APP_DELEGATE!.window?!.rootViewController?.presentedViewController)!
if presentedModalVC == true {
while((presentedModalVC.presentedViewController) != nil){
presentedModalVC = presentedModalVC.presentedViewController!
}
presentedModalVC.presentViewController(viewControllerToPresent, animated: flag, completion: nil)
}
else{
APP_DELEGATE?.window!!.rootViewController?.presentViewController(viewControllerToPresent, animated: flag, completion: nil)
}
}
I tried this but it also needs a UIViewController in its parameters where I have UIDocumentInteractionController
I know these function cannot accept UIDocumentInteractionController type viewController.
here is my whole swift code:
// KPIViewController.swift
// download
//
// Created by me on 15/03/2016.
// Copyright © 2016 me. All rights reserved.
//
import UIKit
class KPIViewController: UIViewController,UITabBarDelegate, NSURLSessionDownloadDelegate, UIDocumentInteractionControllerDelegate{
#IBOutlet weak var tabBar1: UITabBar!
#IBOutlet weak var login_Item: UITabBarItem!
#IBOutlet weak var QAreport_Item: UITabBarItem!
#IBOutlet weak var KpiWebView: UIWebView!
#IBOutlet weak var progressView: UIProgressView!
var downloadTask: NSURLSessionDownloadTask!
var backgroundSession: NSURLSession!
var downloadReport:Bool!
var AuditCodeOfDashboardCell:String?
var AuditCodeForPDF:String?
let isDirectory: ObjCBool = false
override func viewDidLoad() {
super.viewDidLoad()
self.progressView.hidden = true
downloadReport = false
// Do any additional setup after loading the view.
self.tabBar1.delegate = self
}
override func viewDidAppear(animated: Bool) {
self.progressView.hidden = true
downloadReport = false
let backgroundSessionConfiguration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("backgroundSession")
backgroundSession = NSURLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
progressView.setProgress(0.0, animated: false)
var requestURL = NSURL!()
var request = NSURLRequest!()
// loading data from web
if AuditCodeOfDashboardCell != nil{
print(self.AuditCodeOfDashboardCell)
requestURL = NSURL(string:“my URL string&\(AuditCodeOfDashboardCell)”)
request = NSURLRequest(URL: requestURL!)
AuditCodeForPDF = AuditCodeOfDashboardCell
AuditCodeOfDashboardCell = nil
}else{
requestURL = NSURL(string:“my URL string”)
request = NSURLRequest(URL: requestURL!)
}
KpiWebView.loadRequest(request)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
print("selected tabItem: \(item.tag)")
switch (item.tag) {
case 1:
let loginVC = self.storyboard!.instantiateViewControllerWithIdentifier("loginViewController") as! LoginView
presentViewController(loginVC, animated: true, completion: nil)
break
case 2:
if AuditCodeForPDF != nil{
downloadReport = true
let url = NSURL(string: “my URL string&\(AuditCodeForPDF)”)!
urlToDownload = url
}
// if let resultController = storyboard!.instantiateViewControllerWithIdentifier(“2”) as? QAReportViewController {
// presentViewController(resultController, animated: true, completion: nil)
// }
break
default:
break
}
if downloadReport == true{
let url = NSURL(string: “my URL string&\(AuditCodeForPDF)”)!
downloadTask = backgroundSession.downloadTaskWithURL(url)
self.progressView.hidden = false
downloadTask.resume()
downloadReport = false
}
}
// - - Handling download file- - - - - - - - -
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask,
didFinishDownloadingToURL location: NSURL){
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let documentDirectoryPath:String = path.first!
let fileManager = NSFileManager()
var destinationURLForFile = NSURL(fileURLWithPath: documentDirectoryPath.stringByAppendingString("/Report.pdf"))
if fileManager.fileExistsAtPath(destinationURLForFile.path!){
// showFileWithPath(destinationURLForFile.path!)
do{
try fileManager.removeItemAtPath(destinationURLForFile.path!)
destinationURLForFile = NSURL(fileURLWithPath: documentDirectoryPath.stringByAppendingString("/Report.pdf"))
}catch{
print(error)
}
}
do {
try fileManager.moveItemAtURL(location, toURL: destinationURLForFile)
// show file
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.showFileWithPath(destinationURLForFile.path!)
})
}catch{
print("An error occurred while moving file to destination url")
}
}
func showFileWithPath(path: String){
let isFileFound:Bool? = NSFileManager.defaultManager().fileExistsAtPath(path)
if isFileFound == true{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let viewer = UIDocumentInteractionController(URL: NSURL(fileURLWithPath: path))
viewer.delegate = self
viewer.presentPreviewAnimated(true)
})
}
}
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64){
progressView.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
}
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController{
return self
}
func documentInteractionControllerDidEndPreview(controller: UIDocumentInteractionController) {
print("document preview ends")
}
}
I cannot find any proper solution that solve my problem. I am new with swift
please anyone on help me. Thanks in advance
UIDocumentInteractionController is not kind of UIViewController. So you cannot present an UIDocumentInteractionController with presentViewController: method.
Checkout https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDocumentInteractionController_class/
You can presenting a document preview or options menus with UIDocumentInteractionController.

Resources