AVPlayer resizeAspect works only properly on iPhone X - ios

resizeAspect as the video gravity only works properly for me, when using an iPhone X.
For some reasons, the black aspect bar gets only added to the top and not to the bottom. This is how it looks like when I'm not using an iPhone X (the image is white)
This is how it should look like:
As you can see, on the iPhone X, everything looks clean and balanced as expected.
This is how I play the video:
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.frame = PreviewLayer.bounds
avPlayerLayer.videoGravity = .resizeAspect //Will automatically add black bars
PreviewLayer.layer.insertSublayer(avPlayerLayer, at: 0)
let playerItem = AVPlayerItem(url: video)
avPlayer?.replaceCurrentItem(with: playerItem)
avPlayer?.play()

Try this steps
1- Check bottom constraint connect with safe are
2- Try this code
import UIKit
import AVKit
class ViewController: UIViewController {
#IBOutlet weak var playerView: UIView!
private var player: AVPlayer!
override func viewDidLoad() {
super.viewDidLoad()
playVideo(from: "Intro.mp4")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func playVideo(from file:String) {
let file = file.components(separatedBy: ".")
guard let path = Bundle.main.path(forResource: file[0], ofType:file[1]) else {
debugPrint( "\(file.joined(separator: ".")) not found")
return
}
player = AVPlayer(url: URL(fileURLWithPath: path))
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { _ in
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
playerLayer.frame = playerView.layer.bounds
playerView.layer.insertSublayer(playerLayer, at: 0)
player.play()
}
}
Result on iphone X
Result on iphone 7
Note : if you need video full view on all devices try to link Bottom
constraint and Top constraint with superview not with safe area

Make your videoLayer View a subview of:
class VideoContainerView: UIView {
var playerLayer: CALayer?
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
playerLayer?.frame = self.bounds
}
}
and add:
self.VideoShareLayer.playerLayer = avPlayerLayer;
before inserting the layer.

Related

Swift -Why aren't CALayer images visible inside the Debug View Hierarchy

I have a video that plays inside a CALayer. Everything works fine but when I go to the Debug View Hierarchy, the CALayer that the video plays in, always shows up blank.
Why can't I see the video frame that is visible on the CALayer inside the Debug View Hierarchy?
class AVPlayerView: UIView {
var playerLayer: CALayer?
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
playerLayer?.frame = self.bounds
}
}
lazy var avPlayerView: AVPlayerView = {
let playerView = AVPlayerView()
playerView.tag = 2
return playerView
}()
func setPlayerAndPlayerLayer(url: URL) {
let asset = AVAsset(url: url)
playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playerItem)
contentView.addSubview(avPlayerView)
avPlayerView.frame = self.bounds
playerLayer.frame = avPlayerView.bounds
avPlayerView.layer.addSublayer(playerLayer!)
avPlayerView.playerLayer = playerLayer
avPlayerView.layoutIfNeeded()
}

How to deactivate AVPlayer in Swift when you navigate away from view

I have a navigation view from a list of video urls to a destination Videoview and then to a video player. The player uses just the AVPlayer (I don't need controls) to automatically play the video when I navigate to it.
However the video does not stop playing when I hit the 'back' button and navigate away from the PlayerView.
Any thoughts on how to fix this are appreciated.
I've included code from the Navigation view, the pass thru VideoView and the PlayerView as context. TIA.
Navigation view
NavigationView {
List(messages, id: \.self) { message in
NavigationLink(destination: VideoView(videoURL: URL(string:"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4")!, previewLength: 15)) {
Text(message)
}
}.navigationBarTitle("Answers")
VideoView code
struct VideoView: UIViewRepresentable {
var videoURL:URL
var previewLength:Double?
func makeUIView(context: Context) -> UIView {
print("making VideoView")
return PlayerView(frame: .zero, url: videoURL, previewLength: previewLength ?? 30)
}
func updateUIView(_ uiView: UIView, context: Context) {
}
Player Snippet
class PlayerView: UIView {
private let playerLayer = AVPlayerLayer()
private var previewTimer:Timer?
var previewLength:Double
init(frame: CGRect, url: URL, previewLength:Double) {
self.previewLength = previewLength
super.init(frame: frame)
// Create the video player using the URL passed in.
let player = AVPlayer(url: url)
player.volume = 2 // Will play audio if you don't set to zero
// This is the AVPlayer approach to playing video with no controls
player.play() // Set to play once created.
// Add the player to our Player Layer
playerLayer.player = player
playerLayer.videoGravity = .resizeAspectFill // Resizes content to fill whole video layer.
playerLayer.backgroundColor = UIColor.black.cgColor
previewTimer = Timer.scheduledTimer(withTimeInterval: previewLength, repeats: false, block: { (timer) in
player.seek(to: CMTime(seconds: 0, preferredTimescale: CMTimeScale(1)))
})
layer.addSublayer(playerLayer)
Here is possible approach - to use presentation mode on update, because in this scenario it is updated due to navigation back.
struct VideoView: UIViewRepresentable {
#Environment(\.presentationMode) var presentation // << here !!
var videoURL:URL
var previewLength:Double?
func makeUIView(context: Context) -> UIView {
print("making VideoView")
return PlayerView(frame: .zero, url: videoURL, previewLength: previewLength ?? 30)
}
func updateUIView(_ playerView: PlayerView, context: Context) {
// update might be called several times, so PlayerView should
// be safe for repeated calls
if presentation.wrappedValue.isPresented {
playerView.play()
} else {
playerView.stop()
}
}
}
and PlayerView should be updated to have access to player
class PlayerView: UIView {
private let playerLayer = AVPlayerLayer()
private var previewTimer:Timer?
var previewLength:Double
private var player: AVPlayer // << make property
init(frame: CGRect, url: URL, previewLength:Double) {
self.previewLength = previewLength
super.init(frame: frame)
// Create the video player using the URL passed in.
player = AVPlayer(url: url)
player.volume = 2 // Will play audio if you don't set to zero
// don't run .play() here !!!
// ... other code
}
func play() {
player.rate = 1.0
}
func stop() {
player.rate = 0.0
}
}

Playing video on login page as full screen

I want to play background video on login screen on swift 3.0. But when I run program, video do not working as full screen. It is workingHow can I fix this ? Here is the storyboard picture.
enter code here
import UIKit
import AVFoundation
class ViewController: UIViewController {
var Player: AVPlayer!
var PlayerLayer: AVPlayerLayer!
var frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let URL:NSURL = NSURL(string: "https://firebasestorage.googleapis.com/v0/b/pattyapp-34c16.appspot.com/o/dog1.mp4?alt=media&token=98ab3c41-c645-4535-a70b-41511b5df602")!
Player = AVPlayer.init(url: URL as URL)
PlayerLayer = AVPlayerLayer(player: Player)
PlayerLayer.videoGravity = AVLayerVideoGravityResizeAspect
PlayerLayer.frame.size = frame.size
Player.actionAtItemEnd = AVPlayerActionAtItemEnd.none
Player.isMuted = true
Player.play()
view.layer.insertSublayer(PlayerLayer, at: 0)
NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachEnd(notification:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: Player.currentItem)
}
func playerItemReachEnd(notification: NSNotification) {
Player.seek(to: kCMTimeZero)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Play video into background you can use the following list of 3rd party library to resolve your problem.
VideoSplashKit
HAPlayerView
Video You are trying to play has dimensions different from layer size. I'd recommend You to try
PlayerLayer.videoGravity = AVLayerVideoGravityResize
or
PlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill

AVPlayer layer not showing video content sometimes

AVPlayer layer not showing video content sometimes but plays audio.
This happens some times not each time
Here is my lines of code :
override func viewDidLoad() {
super.viewDidLoad()
self.tempVideoPath = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("tmpMov.mov")
self.player = AVPlayer(url: self.tempVideoPath!)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.playerLayer = AVPlayerLayer(player: self.player)
self.playerLayer?.frame = self.videoPlayer.bounds
self.playerLayer?.backgroundColor = UIColor.yellow.cgColor
self.videoPlayer.layer.addSublayer(playerLayer!)
print("player = \(playerLayer?.bounds)")
}
Help me to solve this issue.
the problem with you code is that the bounds of videoPlayer can change.
So you need to add the following code to your view controller to change AVPlayerLayer's frame accordingly:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
playerLayer?.frame = view.bounds
}
Hope it helps.
Although the videoplayer has been initialized correctly or created from storyboard, it seems that the layer is non-existent and creates this annoying problem.
At the beginning of your viewDidAppear first, try to create the layer , than add to it>
let layer = CALayer()
self.videoPlayer.layer = layer
//self.playerView.wantsLayer = true // this line is only for osx

objects of view controller hide behind of video background in swift

i want create a login page for my app, in this case i want to use a video for background of login page, like vine,spotify,uber,...
i play video without problem and add some button and lable in storyboard to my view controllerŲŒ after run app i can not see objects of view controller
i just see video and objects hide behind of video background how can fix this problem,,,
thanks for helping
this is my code for playing video,,,
lazy var playerLayer:AVPlayerLayer = {
let player = AVPlayer(URL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("BWWalkthrough", ofType: "mp4")!))
player.muted = true
player.allowsExternalPlayback = false
player.appliesMediaSelectionCriteriaAutomatically = false
var error:NSError?
// This is needed so it would not cut off users audio (if listening to music etc.
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
} catch var error1 as NSError {
error = error1
} catch {
fatalError()
}
if error != nil {
print(error)
}
var playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.frame
playerLayer.videoGravity = "AVLayerVideoGravityResizeAspectFill"
playerLayer.backgroundColor = UIColor.blackColor().CGColor
player.play()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"playerDidReachEnd", name:AVPlayerItemDidPlayToEndTimeNotification, object:nil)
return playerLayer
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.layer.addSublayer(self.playerLayer)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
playerLayer.frame = self.view.frame
}
func playerDidReachEnd(){
self.playerLayer.player!.seekToTime(kCMTimeZero)
self.playerLayer.player!.play()
}
You need to add your playerLayer below all the views created when your storyboard loads. Replacing
self.view.layer.addSublayer(self.playerLayer)
with
self.view.layer.insertSublayer(self.playerLayer, atIndex:0)
should do the job, unless there are any subviews of the view controller view that you actually want to be behind the video layer.

Resources