Displays the black screen just before the video is loaded - swift

I have 1 AVPlayerViewController and AVPlayer I added the video to it and it worked. But when starting to load video there is a problem that a black screen appears for about 0.5s then the new video can start. I have added AVPlayerViewController to a UIView. I have looked at the background for the UIView medium with the color of the video but every time it loads it displays a black screen then it can start the video. can someone help me. this is my code
class WalkViewController: UIViewController{
#IBOutlet weak var view_player: UIView!
var avPlayer: AVPlayer!
let avPlayerController = AVPlayerViewController()
var sourc_video : String!
var filepath: String!
var fileURL: NSURL!
override func viewDidLoad() {
//video view payer
self.filepath = NSBundle.mainBundle().pathForResource(arr_video[0], ofType: "mp4")
self.fileURL = NSURL.init(fileURLWithPath:filepath!)
self.avPlayer = AVPlayer(URL: fileURL)
self.avPlayerController.player = avPlayer
self.avPlayerController.view.frame = CGRect(x: 0, y: 0, width: 316, height: 316)
self.avPlayerController.showsPlaybackControls = false
self.avPlayerController.player?.play()
avPlayerController.prepareForInterfaceBuilder()
self.addChildViewController(avPlayerController)
self.view_player.addSubview(avPlayerController.view)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

First of all, your code is wrong. You must call didMoveToParentViewController after putting a child view controller’s view into the interface.
Second, video takes time to prepare. Put the view into the interface but hide it. Use KVO to observe isReadyForDisplay. When it becomes true, now show the view.

Related

Playback buttons do not appear in an AVPlayer after adding a layer - iOS 16

I am using an AVPlayer to present a video. The app has only one .mp4 but for a different use case, the same video needs to get flipped.
The buttons are there and completely functional, you can press the play and the 15 seconds forward/backward buttons but they don't appear on the screen (4th video in the attached image)
The issue seems to be that the flip layer I am adding overlays the new layout buttons.
The potential fix I was thinking of is to flip the video before adding it to the player.
Do you know if there is a straightforward solution for this?
Maybe there is an easy way to keep the iOS 15 playback button layout?
The code the app is using to flip the video is as follows:
#IBAction func pressButton(_ sender: Any) {
guard let path = Bundle.main.path(forResource: "sample-5s", ofType:"mp4") else {
return
}
let avPlayer = AVPlayer(url: URL(fileURLWithPath: path))
let avPlayerController = AVPlayerViewController()
present(avPlayerController, animated: true, completion: {
let flippedLayer = AVPlayerLayer(player: avPlayer)
let transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
flippedLayer.frame = (avPlayerController as UIViewController).view.frame
flippedLayer.setAffineTransform(transform)
(avPlayerController as UIViewController).view.layer.addSublayer(flippedLayer)
avPlayerController.player = avPlayer
avPlayer.play()
})
}
Found a solution. It's all about adding AVPlayerViewController inside the parent view controller. Assume that you have a AVPlayerViewController inside a viewController. So first in viewDidLoad make sure to add it as a child.
var playerController = AVPlayerViewController()
lazy var playerBaseView: UIView = {
let view = UIView()
view.backgroundColor = .clear
return view
}()
override func viewDidLoad() {
// Make your player parent view constraints or do it on Storyboard
playerBaseView.snp.makeConstraints { (make) in
make.edges.equalTo(view.safeAreaLayoutGuide.snp.edges)
}
addChild(playerController) // Add as a child
playerBaseView.addSubview(playerController.view)
// Make your playerController view constraints or do it on Storyboard
playerController.view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
For Swift UI
CustomVideoPlayer()
.compositingGroup()
struct CustomVideoPlayer : UIViewControllerRepresentable {
var player: AVPlayer
func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
}
Fixed problem for me in IOS 16 Swift UI

Video doesn't play full screen in a given view in swift. Need help fixing it

I have a view that plays video in app.
The constraints given are correct but some how in devices like iPhone 11, iPhone 11 pro the view leaves some space at trailing end.
Adding screenshots for reference.
*Need help fixing the video display to full view.
Constraints:
Align Trailing - Safe area
Align Top - Safe area
Align Leading - Safe area
Height Equals - 150
Code:
import UIKit
import AVFoundation
class VideoViewController: UIViewController {
#IBOutlet weak var videoView: UIView!
var player: AVQueuePlayer!
var playerItem: AVPlayerItem!
var playerLooper: AVPlayerLooper!
override func viewDidLoad() {
super.viewDidLoad()
videoView()
}
func videoViewForLogin() {
//video
guard let url = Bundle.main.url(forResource: "splashpanoslice", withExtension: "mp4") else {return}
let playerPath = AVPlayer(url: url)
player = AVQueuePlayer()
let playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: url)
let duration = Int64( ( (Float64(CMTimeGetSeconds(AVAsset(url: url).duration)) * 10.0) - 1) / 10.0 )
playerLayer.frame = videoView.bounds
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem, timeRange: CMTimeRange(start: CMTime.zero, end: CMTimeMake(value: duration, timescale: 1)) )
playerLayer.videoGravity = .resizeAspectFill
videoView.layer.insertSublayer(playerLayer, at: 1)
player.play()
}
}
The function code is taken from a reference - How do i make a looping video in swift
Since you're using frames, I think you should try moving layout code later than viewDidLoad(). Perhaps, viewDidLayoutSubviews(), when the view size is determined and also invalidated when displaying parameters are changed (e.g. the screen orientation has changed).

How to add a video in the UI in Swift

I want to be able to add a video (called "Logo-Animation4.mp4") into the UI in Swift, just like you can a UIImage in a UIImageView.
Right now, I've tried just putting an AVPlayer on the view, but it comes up with the default iOS AVPlayer, which contains the regular fullscreen, controls, and volume stuff that I don't want. I just want the video to play in the UI without any way to have the user interact with it.
I've thought about animating the video using a regular UIImageView's animation feature, but my video isn't that short, and it would be hard to get every single frame, and input it into the code.
How should I go about doing this?
To achieve what you are asking, you'll need to use AVPlayerLayer
Add a UIView outlet
#IBOutlet weak var videoView: UIView!
Import AVFoundation
Create player variables
var player : AVPlayer!
var avPlayerLayer : AVPlayerLayer!
Add the following code to your viewDidLoad
guard let path = Bundle.main.path(forResource: "Logo-Animation4", ofType:"mp4") else {
debugPrint("Logo-Animation4.mp4 not found")
return
}
player = AVPlayer(url: URL(fileURLWithPath: path))
avPlayerLayer = AVPlayerLayer(player: player)
avPlayerLayer.videoGravity = AVLayerVideoGravity.resize
videoView.layer.addSublayer(avPlayerLayer)
player.play()
Add this method
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
avPlayerLayer.frame = videoView.layer.bounds
}

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

Show a GIF image using NSImageView

I want to show an animated gif image in my cocoa application.
I dragged the gif into Assets.xcassets in XCode. I was hoping that NSImageView can show a gif out of the box, so I tried the following code.
let imageView = NSImageView(frame: NSRect(x: 0, y: 0, width: 512, height: 512))
imageView.canDrawSubviewsIntoLayer = true
imageView.imageScaling = .ScaleNone
imageView.animates = true
imageView.image = NSImage(named: "loading-animation")
window.contentView?.addSubview(imageView)
The image does not show up. The above code works with a png image. How do I get this to work?
For me it works only if there is nothing set in the Attribute Inspector and with the code like that (hope, it will be useful for someone):
class FirstViewController: NSViewController {
#IBOutlet weak var imgImage1: NSImageView!
override func viewDidLoad() {
super.viewDidLoad()
let imgImage1 = NSImageView(frame: NSRect(x: 407, y: 474, width: 92, height: 74))
imgImage1.canDrawSubviewsIntoLayer = true
imgImage1.imageScaling = .scaleProportionallyDown
imgImage1.animates = true
imgImage1.image = NSImage(named: "mygif")
self.view.addSubview(imgImage1)
}
Enjoy :)
Xcode 12
Just add your gif file in your project (Not in Assets)
Add an NSImageView in your view controller.
Set the image name in the storyboard as your gif file name.
Tap on Animates property.
Answering my own question.
I was able to make a gif display in my app when I get the gif from a URL e.g.
imageView.image = NSImage(byReferencingURL: yourgifurl)
So, I figured there was something wrong with the way I copied the image into my project. Instead of putting the image in Assets.xcassets, I put it with the rest of source code and the gif shows up :) (but the animation speed seems to be very slow)
Suppose you have an animated gif resource named universe in an Assets.xcassets and NSImageView connected by IBOutlet named target:
...
#IBOutlet weak var target: NSImageView!
...
func applicationDidFinishLaunching(_ aNotification: Notification) {
...
target.animates = true
target.image = NSImage(data: NSDataAsset(name: "universe")!.data)
...
}
NOTE:
target.image = NSImage(named: "universe") // WILL NOT WORK!