Play Icon missing in AV Player iOS 14 - swift

I am working on a swift project, using XCode 12 and after the iOS 14 upgrade, the play icon is missing from AVPlayer. The issue is very weird
When I load the app for the first time I am able to see the play icon (ScreenShot1). After going back to the home screen and coming back, the play icon is not visible (ScreenShot2).
ScreenShot1
ScreenShot2
Currently, AVPlayer is embedded inside UIImage View. Below is the Code
imageView.isUserInteractionEnabled = true
mediaPlayer.view.translatesAutoresizingMaskIntoConstraints = false
imageView.addSubview(mediaPlayer.view)
mediaPlayer.view.pin(to: imageView)
guard let itemURL = URL(string: asset.AssetURL) else { return }
let item = AVPlayerItem(url: itemURL)
let player = AVPlayer(playerItem: item)
mediaPlayer.player = player
mediaPlayer.videoGravity = .resizeAspectFill
mediaPlayer.entersFullScreenWhenPlaybackBegins = true
mediaPlayer.exitsFullScreenWhenPlaybackEnds = true

Related

Duration of GIF in swift

I am setting GIF in UIImageView as per following code in Splash Screen.
let url = Bundle.main.url(forResource: "Splash", withExtension: "gif")
self.imgGifSplash.sd_setImage(with: url!, completed: { (img, error, type, url) in
// this block is called when Gif is loaded in UIImageView, not when image is completed playing once.
DispatchQueue.main.asyncAfter(deadline: .now() + 3.2) { // Change `2.0` to the desired number of seconds.
if let _ = UserData.shared.loginData?.token {
let VC = mainStoryboard.instantiateViewController(withIdentifier: "HomeVCNav") as! UINavigationController
objAppDelegate.window?.rootViewController = VC
}
else
{
let VC = mainStoryboard.instantiateViewController(withIdentifier: "LoginNav") as! UINavigationController
objAppDelegate.window?.rootViewController = VC
}
}
}
I want Gif to be played only once.. So i tried using timer of 3.2 seconds but when i am using different devices then i am getting different results like sometimes it played only once, sometime twice, sometime not even completing once.
How can i achieve a perfect animated splash screen that plays GIF only once ?
Hope to get perfect answer. Thanks in advance.

AV Player not working in a container view Swift

I am trying to get a video to play within a container view controller. I would like the video to play only within the container view but have the option to full screen it while playing.
I have a piece of code that can play a video with the screen automatically going full screen using an ‘off container’ button but, I cannot get the video to play within the container. Also, I am unaware of how to use the play button showing on the container view in my image above. The AV Player controller is embedded in the container.
Code below:
#IBAction func playButton(_ sender: Any) {
if let path = Bundle.main.path(forResource: "SampleVideo", ofType: "mp4") {
let video = AVPlayer(url: URL(fileURLWithPath: path))
let videoPlayer = PlayerViewController()
videoPlayer.player = video
present(videoPlayer, animated: true, completion: {
video.play()
})
}
}
No longer an issue. I found out I should have been using AV Player Layer. Now I have the video playing in a UIImageView and is doing what I needed.

Swift Mac OS - How to play another video/change the URL of the video with AVPlayer upon a button click?

I am new to swift and I am trying to make a Mac OS app that loops a video from the app's resources using AVPlayer as the background of the window once the app has been launched. When the user selects a menu item/clicks a button the background video will instantly change to a different video from the app's resources and start looping that video as the window's background.
I was able to play the first video once the app launches following this tutorial: (https://youtu.be/QgeQc587w70) and I also successfully made the video loop itself seamlessly following this post: (Looping AVPlayer seamlessly).
The problem I am now facing is changing the video to the other one once a menu item was selected/a button was clicked. The approach I was going for is to change the url and create a new AVPlayer using the new URL and affect it to the playerView.player following this post: (Swift How to update url of video in AVPlayer when clicking on a button?). However every time the menu item is selected the app crashes with the error "thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)". This is apparently caused by the value of playerView being nil. I don't really understand the reason for this as playerView is an AVPlayerView object that I created using the xib file and linked to the swift file by control-dragging and I couldn't seem to find another appropriate method of doing the thing I wanted to do. If you know the reason for this and the way of fixing it please provide me some help or if you know a better method of doing what I've mention above please tell me as well. Any help would be much appreciated!
My code is as follow, the line that crashes the app is at the bottom:
import Cocoa
import AppKit
import AVKit
import AVFoundation
struct videoVariables {
static var videoName = "Test_Video" //declaring the video name as a global variable
}
var videoIsPlaying = true
var theURL = Bundle.main.url(forResource:videoVariables.videoName, withExtension: "mp4") //creating the video url
var player = AVPlayer.init(url: theURL!)
class BackgroundWindow: NSWindowController {
#IBOutlet weak var playerView: AVPlayerView! // AVPlayerView Linked using control-drag from xib file
#IBOutlet var mainWindow: NSWindow!
#IBOutlet weak var TempBG: NSImageView!
override var windowNibName : String! {
return "BackgroundWindow"
}
//function used for resizing the temporary background image and the playerView to the window’s size
func resizeBG() {
var scrn: NSScreen = NSScreen.main()!
var rect: NSRect = scrn.frame
var height = rect.size.height
var width = rect.size.width
TempBG.setFrameSize(NSSize(width: Int(width), height: Int(height)))
TempBG.frame.origin = CGPoint(x: 0, y: 0)
playerView!.setFrameSize(NSSize(width: Int(width), height: Int(height)))
playerView!.frame.origin = CGPoint(x: 0, y: 0)
}
override func windowDidLoad() {
super.windowDidLoad()
self.window?.titleVisibility = NSWindowTitleVisibility.hidden //hide window’s title
self.window?.styleMask = NSBorderlessWindowMask //hide window’s border
self.window?.hasShadow = false //hide window’s shadow
self.window?.level = Int(CGWindowLevelForKey(CGWindowLevelKey.desktopWindow)) //set window’s layer as desktopWindow layer
self.window?.center()
self.window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
if let screen = NSScreen.main() {
self.window?.setFrame(screen.visibleFrame, display: true, animate: false) //resizing the window to cover the whole screen
}
resizeBG() //resizing the temporary background image and the playerView to the window’s size
startVideo() //start playing and loop the first video as the window’s background
}
//function used for starting the video again once it has been played fully
func playerItemDidReachEnd(notification: NSNotification) {
playerView.player?.seek(to: kCMTimeZero)
playerView.player?.play()
}
//function used for starting and looping the video
func startVideo() {
//set the seeking time to be 2ms ahead to prevent a black screen every time the video loops
let playAhead = CMTimeMake(2, 100);
//loops the video
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object:
playerView.player?.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.playerView.player?.seek(to: playAhead)
self.playerView.player?.play()
}
})
var playerLayer: AVPlayerLayer?
playerLayer = AVPlayerLayer(player: player)
playerView?.player = player
print(playerView?.player)
playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
player.play()
}
//changing the url to the new url and create a new AVPlayer then affect it to the playerView.player once the menu item is being selected
#IBAction func renderBG(_ sender: NSMenuItem) {
videoVariables.videoName = "Test_Video_2"
var theNewURL = Bundle.main.url(forResource:videoVariables.videoName, withExtension: "mp4")
player = AVPlayer.init(url: theNewURL!)
//!!this line crashes the app with the error "thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)" every time the menu item is being selected!!
playerView.player = player
}
}
Additionally, the background video is not supposed to be interactive(E.g. User cannot pause/ fast-forward the video), so any issues that might be caused by user interactivity can be ignored. The purpose of the app is to play a video on the user's desktop creating the exact same effect of running the command:
"/System/Library/Frameworks/ScreenSaver.framework/Resources/
ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background" in terminal.
Any help would be much appreciated!
You don't need to create AVPlayer from url. There is AVPlayerItem class to manipulate player playback queue.
let firstAsset = AVURLAsset(url: firstVideoUrl)
let firstPlayerItem = AVPlayerItem(asset: firstAsset)
let player = AVPlayer(playerItem: firstPlayerItem)
let secondAsset = AVURLAsset(url: secondVideoUrl)
let secondPlayerItem = AVPlayerItem(asset: secondAsset)
player.replaceCurrentItem(with: secondPlayerItem)
Docs about AVPlayerItem

How can I play an intro video the first time I launch an iPhone app using swift (NOT objective-c)?

I want to play an intro video for my iPhone app the first time it launches. I have it set up to detect that it's the first launch, but how do I play the video from the app (not a url) using SWIFT? Do I set it up in the first ViewController file or in the AppDelegate? I've tried using MPMoviePlayerController, but I just can't seem to get it to actually play. Thank you for your help!
I had this same trouble. Here's my solution:
func playVideo() {
let path = NSBundle.mainBundle().pathForResource("video", 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)
}
}
Don't forget to import MediaPlayer and declare your variable - var moviePlayer: MPMoviePlayerController?
Then call your function when you want to play it playVideo()
See my question for how to dismiss it - In iOS how do I programmatically add a video view and then remove it after it finishes playing using Swift?
I got a really helpful answer there.
Also notice that I disabled my controllers because I don't want it to be seen.

Disable Audio (and interruption) with MPMoviePlayerController using Swift

At the moment, this is how I'm playing a video on the subview of my UIViewController:
override func viewDidAppear(animated: Bool) {
let filePath = NSBundle.mainBundle().pathForResource("musicvideo", ofType: "mp4")
self.moviePlayerController.contentURL = NSURL.fileURLWithPath(filePath)
self.moviePlayerController.play()
self.moviePlayerController.repeatMode = .One
self.moviePlayerController.view.frame = self.view.bounds
self.moviePlayerController.scalingMode = .AspectFill
self.moviePlayerController.controlStyle = .None
self.moviePlayerController.allowsAirPlay = false
self.view.addSubview(self.moviePlayerController.view)
}
I've read on ways to disable the audio by doing the following below (none of which work, at all). Keep in mind I'm trying to disable it to the point of not interrupting the current music playing via the Music app, Spotify, etc.
// Playing media items with the applicationMusicPlayer will restore the user's Music state after the application quits.
// The current volume of playing music, in the range of 0.0 to 1.0.
// This property is deprecated -- use MPVolumeView for volume control instead.
1) MPMusicPlayerController.applicationMusicPlayer().volume = 0
2) MPVolumeView doesn't even have a setting for setting the actual volume? It's a control.
3) self.moviePlayerController.useApplicationAudioSession = false
So I found this answer.
This is my Swift code that I ended up going with. I then used an AVPlayerLayer to add to the view as a sublayer, which works perfectly.
Thanks to the OP who managed to get a hold of an Apple technician and provided the original Objective-C code.
The only problems I'm facing now is that it:
1) Interrupts current music playback, whether it's from Music, Spotify, etc.
2) Video stops playing if I close the app and open it up again.
override func viewDidAppear(animated: Bool) {
let filePath = NSBundle.mainBundle().pathForResource("musicvideo", ofType: "mp4")
var asset: AVURLAsset?
asset = AVURLAsset.URLAssetWithURL(NSURL.fileURLWithPath(filePath), options: nil)
var audioTracks = NSArray()
audioTracks = asset!.tracksWithMediaType(AVMediaTypeAudio)
// Mute all the audio tracks
let allAudioParams = NSMutableArray()
for track: AnyObject in audioTracks {
// AVAssetTrack
let audioInputParams = AVMutableAudioMixInputParameters()
audioInputParams.setVolume(0.0, atTime: kCMTimeZero)
audioInputParams.trackID = track.trackID
allAudioParams.addObject(audioInputParams)
}
let audioZeroMix = AVMutableAudioMix()
audioZeroMix.inputParameters = allAudioParams
// Create a player item
let playerItem = AVPlayerItem(asset: asset)
playerItem.audioMix = audioZeroMix
// Create a new Player, and set the player to use the player item
// with the muted audio mix
let player = AVPlayer.playerWithPlayerItem(playerItem) as AVPlayer
player.play()
let layer = AVPlayerLayer(player: player)
player.actionAtItemEnd = .None
layer.frame = self.view.bounds
layer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.view.layer.addSublayer(layer)
}