Please see below code. I am trying to remove the video subview from view when the 'done' button is pressed or video stops playing. I show no errors in the code but the removeFromSubview method does not seem to be working. I am not sure if my syntax is wrong or if it is something to do with having the movieplayer code within the IBAction method and the moviePlayBackDidFinish outside below the viewDidLoad. Any advise much appreciated. Thanks
import Foundation
import UIKit
import MediaPlayer
class VideoViewController: UIViewController {
var moviePlayer:MPMoviePlayerController!
#IBAction func videoLaunch(sender: AnyObject) {
playVideo()
}
func playVideo() {
let path = NSBundle.mainBundle().pathForResource("MyVideo", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerController(contentURL: url)
if let player = moviePlayer {
player.view.frame = self.view.bounds
moviePlayer?.controlStyle = MPMovieControlStyle.Fullscreen
player.prepareToPlay()
self.view.addSubview(player.view)
}
}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "moviePlayBackDidFinish:",
name: MPMoviePlayerPlaybackDidFinishNotification,
object: moviePlayer)
func moviePlayBackDidFinish(notification: NSNotification){
self.view.removeFromSuperview()
}
}
}
You are trying to remove self.view not moviePlayer.view.
Change your moviePlayBackDidFinish code to:
func moviePlayBackDidFinish(notification: NSNotification)
{
if let player = moviePlayer
{
player.view.removeFromSuperview()
}
}
Related
Im currently using 'AVKit' and 'AVFoundation' to enable the video playing through my app but I can only done by starting the video by tapping a button. But I am currently facing a problem of trying to making an automatic function like playing the video footage right after we start an app on an iOS device.
override func viewDidLoad()
{
super.viewDidLoad()
self.playingVideo()
self.nowPlaying(self)
}
#IBAction func nowPlaying(_ sender: Any)
{
self.present(self.playerController, animated: true, completion:{
self.playerController.player?.play()
})
after I compiled it, the system printed out:
Warning: Attempt to present on whose view is not in the window hierarchy!
Please try following working code.
import AVFoundation
import UIKit
class SomeViewController: UIViewController {
func openVideo() {
let playerController = SomeMediaPlayer()
playerController.audioURL = URL(string: "Some audio/video url string")
self.present(playerController, animated: true, completion: nil)
}
}
class SomeMediaPlayer: UIViewController {
public var audioURL:URL!
private var player = AVPlayer()
private var playerLayer: AVPlayerLayer!
override func viewDidLoad() {
super.viewDidLoad()
self.playerLayer = AVPlayerLayer(player: self.player)
self.view.layer.insertSublayer(self.playerLayer, at: 0)
let playerItem = AVPlayerItem(url: self.audioURL)
self.player.replaceCurrentItem(with: playerItem)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.player.play()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.playerLayer.frame = self.view.bounds
}
// Force the view into landscape mode if you need to
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get {
return .landscape
}
}
}
I have programmatically added an AVPlayerViewController to a UIViewController. I am able to receive the notification when the player is finished playing (playerDidFinishPlaying). I would also like to know if a user has touched the screen while the video is playing and I have not found any related notifications.
The solution is to create a Base class of AVPlayerViewController and override touchesBegan(_:with:) method:
Swift 2:
Custom Base Class:
class CustomAVPlayerViewController: AVPlayerViewController {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("touchesBegan")
}
}
ViewController:
let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(URL: videoURL!)
let playerViewController = CustomAVPlayerViewController()
playerViewController.player = player
self.presentViewController(playerViewController, animated: true) {
playerViewController.player!.play()
}
Swift 3:
Custom Base Class:
class CustomAVPlayerViewController: AVPlayerViewController {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesBegan")
}
}
View Controller:
let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL!)
let playerViewController = CustomAVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
Don't forget to import AVKit and import AVFoundation.
Each time you tap on the playerViewController, "touchesBegan" will be printed.
I had this same problem. The contentOverlayView is only available in tvos, so that was not an option.
I ended up adding a UIView over the UIImageView that I added the AVPlayer to. I set the background color to clear on the UIView, so it's not visible, but can receive gestures. This provides a target for the tap gesture recognizer.
I resolve the same. Subclassing AVPlayerViewController will work on iOS 11.4.1 but not on iOS 12 an above. So the solution for this is add subview on playerviewcontroller contentoverlayview and then on that subview you can add any gesture or button for detecting the touch. Here is the code snippet for the same::
// This notification is added for continuous playing of the video you can remove this in case you need video is played only once.
private func playVideo() {
guard let path = Bundle.main.path(forResource: "BG01", ofType:"mp4") else {
debugPrint("video.m4v not found")
return
}
self.player = AVPlayer(url: URL(fileURLWithPath: path))
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: kCMTimeZero)
self?.player?.play()
}
let playerController : AVPlayerViewController? = AVPlayerViewController()
let btn : UIButton = UIButton()
btn.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
btn.addTarget(self, action: #selector(touchDetect), for: .touchUpInside)
btn.backgroundColor = UIColor.clear
playerController?.contentOverlayView?.addSubview(btn)
// playerController?.homeVCProtocolDelegate = self as HomeVCProtocol
playerController?.player = player
playerController?.showsPlaybackControls = false
self.player?.play()
present(playerController!, animated: false) {
self.player?.play()
}
}
#objc func touchDetect()
{
// Here you will get the call
}
I'm new to Swift and am trying to add a video to the view and then remove it when my "stopScreenSaver" notification is dispatched. All seems to work well except for when I go to remove the video layer (playerLayer.removeFromSuperlayer()).
Any guidance would be appreciated. I feel like I'm missing some basic concept here for adding and removing the layer!
import UIKit
import AVFoundation
import QuartzCore
import CoreMedia
class ViewController: UIViewController {
let contentURL = NSBundle.mainBundle().URLForResource("testvideo", withExtension: "mp4")
var player = AVPlayer()
var playerLayer = AVPlayerLayer()
let screenSize : CGRect = UIScreen.mainScreen().bounds
override func viewDidLoad() {
super.viewDidLoad()
// Used for starting and stopping the videos related to the screen saver
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playScreenSaver:", name: "playScreenSaverID", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "stopScreenSaver:", name: "stopScreenSaverID", object: nil)
}
override func viewDidAppear(animated: Bool) {
// Player
player = AVPlayer(URL: contentURL!)
// Layer for display… Video plays at the full size of the iPad
playerLayer = AVPlayerLayer(player: player)
var view = UIView(frame: CGRectMake(0, 0, screenSize.width, screenSize.height))
self.view.layer.addSublayer(playerLayer)
playerLayer.frame = view.bounds
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func playScreenSaver(notification: NSNotification){
print("play")
dispatch_async(dispatch_get_main_queue()) {
self.view.layer.addSublayer(self.playerLayer!)
self.player!.play()
}
}
func stopScreenSaver(notification: NSNotification){
print("pause")
dispatch_async(dispatch_get_main_queue()) {
self.player!.pause()
self.playerLayer!.removeFromSuperlayer()
}
}
}
Using the dispatch fixed the issue I was having.
dispatch_async(dispatch_get_main_queue()) {
self.player!.pause()
self.playerLayer!.removeFromSuperlayer()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
DispatchQueue.main.async {
self.player.currentItem?.removeObserver(self, forKeyPath: "duration", context: nil)
self.player!.pause()
self.playerLayer!.removeFromSuperlayer()
}
}
Try this
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
DispatchQueue.main.async {
self.player?.pause()
self.playerLayer?.removeFromSuperlayer()
}
}
My movie file starts no problem. The done button does not dismiss the video content. No idea why? Also, Fast Forward and Rewind buttons just cause a black screen. I don't think I am using the notification functions correctly?
import Foundation
import UIKit
import MediaPlayer
class VideoViewController: UIViewController {
var moviePlayer:MPMoviePlayerController!
#IBAction func videoLaunch(sender: AnyObject) {
playVideo()
}
func playVideo() {
let path = NSBundle.mainBundle().pathForResource("MyVideo", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerController(contentURL: url)
if let player = moviePlayer {
player.view.frame = self.view.bounds
moviePlayer?.controlStyle = MPMovieControlStyle.Fullscreen
player.prepareToPlay()
self.view.addSubview(player.view)
}
}
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "moviePlayBackDidFinish:",
name: MPMoviePlayerPlaybackDidFinishNotification,
object: moviePlayer)
func moviePlayBackDidFinish(notification: NSNotification){
self.view.removeFromSuperview()
}
}
}
You are adding player view as subview. You should remove it (removeFromSuperview) after done button pressed. Use notifications to listen for playback finish:
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "moviePlayBackDidFinish:",
name: MPMoviePlayerPlaybackDidFinishNotification,
object: moviePlayer)
and moviePlayBackDidFinish:
func moviePlayBackDidFinish(notification: NSNotification){
// remove from superview
}
You should remove moviePlayer from your superview like this:
func moviePlayBackDidFinish(notification: NSNotification){
let moviePlayer:MPMoviePlayerController = notif.object as! MPMoviePlayerController
moviePlayer.view.removeFromSuperview()
}
Because in your case you remove self.view
I'm trying to make a little intro video for an app before you land on the main view. Code as follows:
import UIKit
import MediaPlayer
class ViewController: UIViewController {
var moviePlayer: MPMoviePlayerController?
//var player: MPMoviePlayerController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
playVideo()
self.view.removeFromSuperview()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func playVideo() {
let path = NSBundle.mainBundle().pathForResource("paint-me_intro", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerController(contentURL: url)
if let player = moviePlayer {
player.view.frame = self.view.bounds
player.controlStyle = .None
player.prepareToPlay()
player.scalingMode = .AspectFit
self.view.addSubview(player.view)
}
}
}
All I want it to do is after the video finishes playing, it needs to go away. That's all. Any help would be greatly appreciated before I smash my face against the wall.
MPMoviePlayerController uses notifications for message passing, unlike the delegate/protocol pattern of so many other classes. Regardless, to answer your question. Add an observer for the appropriate notification in your view did load, and point to a function which removes the view.
Adding observer
NSNotificationCenter.defaultCenter().addObserver(self, selector: "movieFinished", name:
MPMoviePlayerPlaybackDidFinishNotification, object: nil)
And the function to remove it.
func movieFinished() {
moviePlayer!.view.removeFromSuperview()
NSNotificationCenter.defaultCenter().removeObserver(self, name: MPMoviePlayerPlaybackDidFinishNotification, object: nil)
}