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.
Related
I've created a mask view for my camera screen that blurs everything except for a small rectangle in the middle. I'm having issues when testing on different screen sizes. I'm testing on an 11 Pro and an 8. If my storyboard view has the proper device selected, the constraints look fine on the device. But if I switch phones without changing in the storyboard, it is incorrect. I'm programmatically creating the mask view. Any help would be much appreciated!
func setupBlur() {
if !UIAccessibility.isReduceTransparencyEnabled {
blurView.backgroundColor = .clear
let blurEffect = UIBlurEffect(style: .dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = blurView.bounds
blurEffectView.translatesAutoresizingMaskIntoConstraints = false
blurEffectView.addConstraints(blurView.constraints)
blurView.addSubview(blurEffectView)
let maskView = UIView(frame: blurView.bounds)
maskView.translatesAutoresizingMaskIntoConstraints = false
maskView.addConstraints(blurView.constraints)
maskView.backgroundColor = .clear
let outerbezierPath = UIBezierPath.init(rect: blurView.bounds)
let croppedOriginX = (blurView.bounds.width / 2) - (captureView.bounds.width / 2)
let croppedOriginY = (blurView.bounds.height / 2) - (captureView.bounds.height / 2)
let frame = CGRect(x: croppedOriginX, y: croppedOriginY, width: captureView.bounds.width, height: captureView.bounds.height)
let innerRectPath = UIBezierPath.init(rect: frame)
outerbezierPath.append(innerRectPath)
outerbezierPath.usesEvenOddFillRule = true
let fillLayer = CAShapeLayer()
fillLayer.fillRule = kCAFillRuleEvenOdd
fillLayer.fillColor = UIColor.green.cgColor // any opaque color would work
fillLayer.path = outerbezierPath.cgPath
maskView.layer.addSublayer(fillLayer)
blurView.mask = maskView;
}
}
This happens because when you call setupBlur() from viewDidLoad, at the beginning blurView.bounds values are from stroyboard which you're working on it, and you are using these bounds to setup blurEffectView.frame before they are adjusted for your device's screen.
This is not the best solution, but I hope it will resolve your problem.
Try this:
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.setupBlur()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.setupBlur()
}
In my case, I've tried viewDidLayoutSubviews() instead of DispatchQue but it didn't work. However, viewDidLoad() worked for me.
override func viewDidAppear() {
self.setupBlur()
}
I am trying to resize uiimage ratio. Right now i am getting image into square not in correct aspect ratio. If i change the width and height uiimage into pickImage.frame.size.width, pickImage.frame.size.hight then UIImage look so large.
If i set pickImage?.contentMode = .scaleAspectFit then image set its position but drop shadow is shown full image view not image picked one.
Here is the screenshots of result i got
And close button should be top left corner, here when i pick image from image picker any other position of close button image set to properly based on landscape or portrait.
Here is the code i used:
func addImage(url : URL) {
let tag = Int(arc4random_uniform(6))
pickImage = UIImageView()
pickImage?.sd_setImage(with:url)
pickImage?.sd_setShowActivityIndicatorView(true)
pickImage?.backgroundColor = UIColor.lightGray
pickImage?.sd_setIndicatorStyle(.gray)
pickImage?.frame = CGRect(x: randomNumber(inRange:
200...Int(touchDrawview.frame.width - 200)), y: Int(getYValue(maxYValue:
Int(touchDrawview.frame.height - 200))), width: 200, height: 200)
pickImage?.autoresizingMask = [.flexibleTopMargin, .flexibleHeight,
.flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin,
.flexibleWidth]
pickImage?.contentMode = .scaleAspectFit
pickImage?.tag = tag
pickImage?.isUserInteractionEnabled = true
let imageclose = UIImage(named: "imageclose")
closeImage = UIImageView(image : imageclose)
closeImage?.frame = CGRect(x: 10, y: 10, width: 30, height: 30)
closeImage?.tag = tag
closeImage?.isHidden = true
closeImage?.isUserInteractionEnabled = true
pickImage?.layer.shadowColor = UIColor.white.cgColor
pickImage?.layer.shadowOffset = CGSize(width: 0, height: 3)
pickImage?.layer.shadowOpacity = 1
pickImage?.layer.shadowRadius = 1.0
pickImage?.clipsToBounds = false
let longGuetureImage = UILongPressGestureRecognizer(target: self, action:
#selector(longPressImage(sender:)))
longGuetureImage.minimumPressDuration = 0.1
pickImage?.isUserInteractionEnabled = true
longGuetureImage.delegate = self
pickImage?.addGestureRecognizer(longGuetureImage)
let panGesture = UIPanGestureRecognizer(target: self, action:
#selector(handlePanImage(recognizer:)))
panGesture.delegate = self
pickImage?.isUserInteractionEnabled = true
pickImage?.addGestureRecognizer(panGesture)
let tapGuetureImage = UITapGestureRecognizer(target: self, action:
#selector(removeImage(sender:)))
tapGuetureImage.delegate = self
closeImage?.addGestureRecognizer(tapGuetureImage)
let tapGueturemainImage = UITapGestureRecognizer(target: self, action:
#selector(selectdragImageTap(_:)))
tapGueturemainImage.delegate = self
pickImage?.addGestureRecognizer(tapGueturemainImage)
let rotate = UIRotationGestureRecognizer(target: self, action:
#selector(handlerotateImage(recognizer:)))
rotate.delegate = self
pickImage?.addGestureRecognizer(rotate)
let pinch = UIPinchGestureRecognizer(target: self, action:
#selector(handlePinchImage(sender:)))
pinch.delegate = self
pickImage?.addGestureRecognizer(pinch)
pickImage?.dropShadowOff()
addPickedImage(image: pickImage!, closeimage: closeImage!,imageType :
PickedType.image.rawValue,imageData: url.absoluteString)
pickImage = nil
closeImage = nil
}
Try This:
pickImage?.contentMode = .scaleAspectFill
pickImage?.clipsToBounds = true
Create a UIView with clipsToBounds = true first, apply shadow on this view and then add your pickimage and button as a subView in this view. because clipsToBounds = true stop dropping shadow on current view.
When the image is resized, the aspect ratio of image may not be the same as the view. There are two approaches for what you are trying to achieve.
Approach 1:
Resize the image to fit the view. Here the aspect ratio may be different than view hence image may get distorted. To resize image, refer below,
Refer:
https://aurvan.github.io/atkit-ios-release/index.html
Class Reference:
https://aurvan.github.io/atkit-ios-release/helpbook/Extensions/UIImage.html
Code:
import ATKit
let anImage :UIImage = UIImage(named: "DefaultAvatar")!
let aResizedImage :UIImage? = anImage.resize(size: CGSize(width: 100.0, height: 200.0), scaleMode: UIImageScaleMode.aspectFit)
Approach 2: Calculate the image size manually and adjust the close button from the horizontal center of the view. I have not tried the code, but something like below should work,
anImageX = (anImageViewWidth - anImageWidth) / 2.0
anImageY = (anImageViewHeight - anImageHeight) / 2.0
Actually, I want shadow at bottom of UIView.
I had tried some code but getting shadow from top side only and I am using swift 3 currently.
Please follow below code :
let horizontalLine = UIView()
horizontalLine.frame = CGRect.zero
horizontalLine.backgroundColor = .lightGray
self.addSubview(horizontalLine)
horizontalLine.layer.shadowColor = UIColor.gray.cgColor
horizontalLine.layer.shadowOpacity = 0.5
horizontalLine.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
horizontalLine.layer.masksToBounds = false
horizontalLine.backgroundColor = .lightGray
horizontalLine.layer.shadowRadius = 5
Also I am using snapkit library for UI Setting:
horizontalLine.snp.makeConstraints{ (make) in
make.height.equalTo(5)
make.width.equalTo(self.snp.width)
make.left.equalTo(self.snp.left)
make.right.equalTo(self.snp.right)
make.bottom.equalTo(self.snp.bottom)
}
How it looks now:
Also, I do have collection view just down that.
And we have one more collection view just backside of that line.
Please guide me guys.
Thanks in advance.
Based on the image you show, it looks like the "shadow" you are seeing is in the cell content of the collection view above your horizontalLine view.
It also looks like the shadow on your horizontalLine view is not visible at all - because its superview is clipping it.
Try this:
// new line
self.clipsToBounds = false
// rest of your code...
let horizontalLine = UIView()
try this..
let horizontalLine = UIView()
horizontalLine.frame = CGRect(x: 150, y: 350, width: 150, height: 150)
horizontalLine.backgroundColor = .lightGray
self.view.addSubview(horizontalLine)
horizontalLine.layer.shadowColor = UIColor.red.cgColor
horizontalLine.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
horizontalLine.layer.shadowOpacity = 1.0
horizontalLine.layer.shadowRadius = 0.0
horizontalLine.layer.masksToBounds = false
horizontalLine.layer.cornerRadius = 4.0
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)
}