I have an app which plays video clips on demand. This has worked well in previous versions of Xcode but I've upgraded to 8.3.3 and am having problems with the AVPlayerViewController.
The video plays and displays correctly. The control bar appears at the bottom of the view but does not respond to taps and, once faded out, does not reappear on tapping the video - unless, that is, I tap near the top left of the view.
My guess is that the actual controls are hidden in some kind of overlay which has the wrong size i.e. it does not properly overlay the whole of the video view. Is there some way to force the AVPlayerViewController to relayout?
I've tried adding:
_playerController!.view.setNeedsLayout()
_playerController!.view.layoutIfNeeded()
But this has no effect.
Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
_player = AVPlayer()
_playerController = AVPlayerViewController()
_playerController!.showsPlaybackControls = true
_playerController!.allowsPictureInPicturePlayback = false
_playerController!.videoGravity = AVLayerVideoGravityResizeAspectFill
_playerController!.player = _player
self.addChildViewController(_playerController!)
videoView.addSubview(_playerController!.view)
...
override func viewDidLayoutSubviews() {
let frame = UIScreen.main.bounds
_vidWidth = frame.width - 256.0
_vidHeight = _vidWidth / 854.0 * 480.0
videoView.frame = CGRect(x: 0, y: 10.0, width: _vidWidth, height: _vidHeight)
videoView.backgroundColor = UIColor.black
_playerController?.view.frame = CGRect(x: 0, y: 0, width: _vidWidth, height: _vidHeight)
...
func playVideo(_ clip: Clip) {
var videoUrl: URL? = nil
if clip.offlineLocation != nil && clip.state == 2 {
_videoPath = clip.offlineLocation!
videoUrl = URL(fileURLWithPath: _videoPath!)
}
else {
_videoPath = "https://xxxx.cloudfront.net/" + clip.location
videoUrl = URL(string: _videoPath!)
}
NSLog(_videoPath!)
let asset = AVURLAsset(url:videoUrl!, options:nil)
let playerItem = AVPlayerItem(asset: asset)
_player!.replaceCurrentItem(with: playerItem)
_player!.play()
}
Related
Ok so in my iOS app there is a screen with an AVPlayerViewController that takes care of playing videos. When this video is played it fills the entire screen however on some videos they video seems stretched as if it is being forced to fit. Is there any way to make the video fit the entire screen without losing the aspect ratio.
The code below is what I use to lay out the AVPlayerController. I figured some error here is the cause of this flaw in aspect ratio.
/
// Setup the view for stories
private func setupViews() {
view.backgroundColor = .clear
// Setup the blur view for loading
blurView = UIVisualEffectView(frame: view.frame)
blurView.effect = UIBlurEffect(style: .regular)
self.view.addSubview(blurView)
// Indicator for loading
let indicatorFrame = CGRect(x: view.center.x - 20, y: view.center.y - 20, width: 40, height: 40)
indicator = UIActivityIndicatorView(frame: indicatorFrame)
indicator.hidesWhenStopped = true
indicator.activityIndicatorViewStyle = .whiteLarge
blurView.contentView.addSubview(indicator)
// Setup the Progress bar
// spb = SegmentedProgressBar(numberOfSegments: allStories.count)
//// spb.frame = CGRect(x: 0, y: 1, width: self.view.frame.width, height: 9)
// self.view.addSubview(spb)
// spb.snp.makeConstraints { (make) in
// make.left.right.equalTo(view.safeAreaLayoutGuide)
// make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(1)
// make.height.equalTo(9)
// }
//
// spb.delegate = self
// Player Controller
playerController = AVPlayerViewController()
playerController.showsPlaybackControls = false
self.addChildViewController(playerController)
// Image view for the story
imageView = UIImageView(frame: self.view.frame)
infoView = UIView(frame: self.view.frame)
infoView.backgroundColor = UIColor.clear
// The image view for the user of the current story
infoImageView = UIImageView()
self.view.addSubview(imageView)
self.view.addSubview(playerController.view)
// self.view.bringSubview(toFront: spb)
self.view.addSubview(infoView)
self.infoView.addSubview(infoImageView)
infoImageView.snp.makeConstraints { (make) in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(2)
make.left.equalTo(view.safeAreaLayoutGuide.snp.left).offset(10)
make.height.width.equalTo(30)
}
infoImageView.layer.cornerRadius = 15
infoImageView.layer.masksToBounds = true
// The name for the user of the current story
infoNameLabel = UILabel()
infoNameLabel.backgroundColor = UIColor.black.withAlphaComponent(0.1)
infoNameLabel.textColor = UIColor.white
infoNameLabel.font = UIFont.boldSystemFont(ofSize: 18)
infoNameLabel.adjustsFontSizeToFitWidth = true
self.infoView.addSubview(infoNameLabel)
infoNameLabel.snp.makeConstraints { (make) in
make.left.equalTo(infoImageView.snp.right).offset(5)
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(2)
make.height.equalTo(30)
make.width.equalTo(80)
}
// Time label for long ago the story was posted
infoTimeLabel = UILabel()
infoTimeLabel.backgroundColor = UIColor.black.withAlphaComponent(0.1)
infoTimeLabel.textColor = UIColor.white
self.infoView.addSubview(infoTimeLabel)
infoTimeLabel.snp.makeConstraints { (make) in
make.left.equalTo(infoNameLabel.snp.right).offset(5)
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(2)
make.height.equalTo(30)
make.width.equalTo(50)
}
infoView.isUserInteractionEnabled = true
infoView.addGestureRecognizer(tapInfoView)
infoView.addGestureRecognizer(swipeInfoView)
infoTimeLabel.layer.cornerRadius = 10
infoTimeLabel.layer.masksToBounds = true
infoTimeLabel.textAlignment = .center
infoNameLabel.layer.cornerRadius = 10
infoNameLabel.layer.masksToBounds = true
infoNameLabel.textAlignment = .center
playerController.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue
playerController.view.frame = view.frame
// spb.durations = durations
}
Any help is greatly appreciated
to keep the video aspect ratio in the defined view frame use
AVLayerVideoGravity.resizeAspect.
For the current case replace
playerController.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue
to
playerController.videoGravity = AVLayerVideoGravity.resizeAspect
I am not sure about it but use this :
playerController.view.frame = view.bounds
Instead of this:
playerController.view.frame = view.frame
The AVLayerVideoGravity.resizeAspectFill.rawValue should do what is says and I never had a problem with it. However, I use the bounds instead of the frame and I remember getting problem when using the frame.
You also have AVLayerVideoGravity.resizeAspect.rawValue that will fit the video instead of filling it.
You can also try setting the frame before the video gravity.
The following code shows my video file in correct zPosition with the other elements I'm working with, creating a background video.
The problem I'm having is that the vertical video (1080x1920 pixels) gets rotated 90 degrees counterclockwise, and is stretched to fit as a landscape video. How can I ensure correct orientation without sacrificing my need to use the SKVideoNode with zPosition?
let videoNode: SKVideoNode? = {
guard let urlString = Bundle.main.path(forResource: "merry", ofType: "mov") else {
return nil
}
let url = URL(fileURLWithPath: urlString)
let item = AVPlayerItem(url: url)
player = AVPlayer(playerItem: item)
return SKVideoNode(avPlayer: player)
}()
videoNode?.position = CGPoint( x: frame.midX, y: frame.midY)
videoNode?.size = self.frame.size
videoNode?.zPosition = 20
addChild((videoNode)!)
player.play()
player.volume = 0
Many thanks!
Got there in the end with a workaround:
// fix to rotate vertical video by 90 degrees and resize to fit....
videoNode?.zRotation = CGFloat(-Double.pi) * 90 / 180
videoNode?.size.width = self.frame.size.height
videoNode?.size.height = self.frame.size.width
I am using the following code to create a vertical scrollview with paging enabled which works perfectly fine. Now I would like to add a subview of the type AVPlayer (?) to homeView, view2, view3. I can put setupCustomPlayer() in the initiateScrollView() function which works very well, too. However, it is not attached to any of the previously-mentioned views. If I try to add a subview using the function addSubview(anything) it tells me a UIView is expected. I need the UIViews as the vertical sliding feed is very important. Does someone know how to add it as a subview? The video is going to cover the entire screen and going to serve as kind of a background video one could say (if you would like to know)
Code:
func setupCustomPlayer() {
print("check")
AVPlayerVC.view.frame = self.view.frame
AVPlayerVC.view.sizeToFit()
AVPlayerVC.showsPlaybackControls = false
self.view.addSubview(AVPlayerVC.view)
let videoURL = NSURL(string: "http://URL")
let player = AVPlayer(url: videoURL! as URL)
AVPlayerVC.player = player
AVPlayerVC.player?.play()
}
func initiateScrollView() {
//create scrollView with paging enabled
let scrollView = UIScrollView(frame: view.bounds)
scrollView.isPagingEnabled = true
view.addSubview(scrollView)
//do not show vertical scroll indicator
scrollView.showsVerticalScrollIndicator = false;
//get page size
let pageSize = view.bounds.size
//individual views
let homeView = UIView()
homeView.backgroundColor = .green
let view2 = UIView()
view2.backgroundColor = .blue
let view3 = UIView()
view3.backgroundColor = .red
//array with individual views
let pagesViews = [homeView, view2, view3]
//amount of views
let numberOfPages = pagesViews.count
//add subviews (pages)
for (pageIndex, page) in pagesViews.enumerated(){
page.frame = CGRect(origin: CGPoint(x:0 , y:CGFloat(pageIndex) * pageSize.height), size: pageSize)
scrollView.addSubview(page)
}
//define size of scrollView
scrollView.contentSize = CGSize(width: pageSize.width, height: pageSize.height * CGFloat(numberOfPages))
}
Could you try this?
func setupCustomPlayer() {
print("check")
let player = AVPlayer(url: yourFileUrl)
var playerLayer: AVPlayerLayer?
playerLayer = AVPlayerLayer(player: player)
playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer!.frame = self.view.frame
self.view.layer.addSublayer(playerLayer!)
player.play()
}
I want to show a video of a topic in top half of the view and its matter in textview in the bottom half of the view. For that video control i want to have the features like play,pause,stop,ff etc. Also i want to play it from local resource as my web services hasn't been setup yet. pls suggest a good solution
I have tried UIWebView and added constraints to webview and textview but for some reason the web view is not showing the video correctly. below is my code
let purl = NSURL(fileURLWithPath: "/Users/Rohit/Desktop/videos/demo/demo/video1.mp4") webView.loadHTMLString("<iframe width = \" \(webView.frame.width) \" height = \"\(webView.frame.height)\" src = \"\(purl)\"></iframe>", baseURL: nil)
webView.backgroundColor = UIColor.green
webView.mediaPlaybackRequiresUserAction = true
webView.scrollView.isScrollEnabled = true
webView.isUserInteractionEnabled = true
Import AVFoundation and AVKit
Then play the video using an URL object (in Swift 3 NSURL is renamed to URL)
let player = AVPlayer(URL: URI)
let controller = AVPlayerViewController()
controller.player = player
self.addChildViewController(controller)
let screenSize = UIScreen.main.bounds.size
let videoFrame = CGRect(x: 0, y: 10, width: screenSize.width, height: (screenSize.height - 10) * 0.5)
controller.view.frame = videoFrame
self.view.addSubview(controller.view)
player.play()
You can use AVPlayerLayer and give it bounds.
private func inits() {
//let rootLayer: CALayer = self.layer
// rootLayer.masksToBounds = true
avPlayerLayer = AVPlayerLayer(player: player)
avPlayerLayer.bounds = self.bounds
// avPlayerLayer.backgroundColor = UIColor.yellowColor().CGColor
self.layer.insertSublayer(avPlayerLayer, atIndex: 0)
}
I created a Video Player using AVPlayer and AVPlayerViewController. I have set "allowsExternalPlayback" property to true and also "usesExternalPlaybackWhileExternalScreenIsActive" property to true. But still I am not getting Airplay Icon in Player Controls.
player = AVPlayer(URL: url!)
player!.allowsExternalPlayback = true
player?.usesExternalPlaybackWhileExternalScreenIsActive = true
I am running my app on ios 9.2.
You need to add an MPVolumeView in order to get this. You can read about this here: https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AirPlayGuide/EnrichYourAppforAirPlay/EnrichYourAppforAirPlay.html
func appleTv()
{
let rect = CGRect(x: -100, y: 0, width: 0, height: 0)
let airplayVolume = MPVolumeView(frame: rect)
airplayVolume.showsVolumeSlider = false
self.view.addSubview(airplayVolume)
for view: UIView in airplayVolume.subviews {
if let button = view as? UIButton {
button.sendActions(for: .touchUpInside)
break
}
}
airplayVolume.removeFromSuperview()
}