Swift mask UIView constraints issues - ios

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()
}

Related

AVPlayer Aspect Ratio

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.

How to make the UIImage edges softer?

I have a UIImage with a UIVisualEffectView on top of it. I want to make the edges softer to give it a shadow effect.
I tried playing around with maskToBounds but it doesn't make a difference.
let weekndImage = UIImage(named: "weeknd.jpg")
let weekndIV = UIImageView(image: weekndImage)
weekndIV.frame.size = CGSize(width: 200, height: 200)
let blur = UIBlurEffect(style: .light)
let blurView = UIVisualEffectView(effect: blur)
blurView.frame = weekndIV.bounds
weekndIV.addSubview(blurView)
weekndIV.layer.cornerRadius = 10
weekndIV.layer.masksToBounds = true
move these two lines after setting the blurView frame and before adding blurView to weekndIV view
weekndIV.layer.cornerRadius = 10
weekndIV.layer.masksToBounds = true
I think you want to have a shadow effect on your view. For that you can access shadowOpacity and shadowRadius from layer of your view
For example in Swift 2:
class MyView : UIView {
override func awakeFromNib() {
layer.cornerRadius = 5.0
layer.shadowOpacity = 0.8
layer.shadowRadius = 5.0
layer.shadowOffset = CGSizeMake(2.0, 2.0)
}
}

Why is my table view shadow scrolling with my table view?

I added shadow to my table view but unfortunately when I scroll through the table view the shadow also moves with the table. The code for adding shadow is as follows:
func addShadow(to myView: UIView){
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: uiView.frame.width, height: uiView.frame.height * 1.1))
uiView.layer.shadowColor = UIColor.black.cgColor
uiView.layer.shadowOffset = CGSize(width: 0.2, height: 0)
uiView.layer.shadowOpacity = 0.5
uiView.layer.shadowRadius = 5.0
uiView.layer.masksToBounds = false
uiView.layer.shadowPath = shadowPath.cgPath
}
Can you please explain to me why is this happening and how to make the shadow stick to its designated location?
Thank you in advance.
If you are adding shadow on tableView, it will scroll along with tableview data. To prevent that you have to add UIView first. Add tableview on that view. Add shadow for the UIView you have taken. It will stick to designated location.
This is my solution in Swift 3 with an UIView and a CAGradientLayer inside.
override func viewWillAppear(_ animated: Bool) {
addShadow(myView: myTab)
}
func addShadow(myView: UIView){
let shadowView = UIView()
shadowView.center = CGPoint(x: myView.frame.minX,y:myView.frame.minY - 15)
shadowView.frame.size = CGSize(width: myView.frame.width, height: 15)
let gradient = CAGradientLayer()
gradient.frame.size = shadowView.frame.size
let stopColor = UIColor.clear.cgColor
let startColor = UIColor.black.withAlphaComponent(0.8).cgColor
gradient.colors = [stopColor,startColor]
gradient.locations = [0.0,1.0]
shadowView.layer.addSublayer(gradient)
view.addSubview(shadowView)
}

Transparent blurred view is not working

I'd like to apply layer on top of ImageView that slightly blurrs it.
I put a view on top of imageView, subclass it as follows:
class BlurredView: UIView {
override func draw(_ rect: CGRect) {
self.backgroundColor = UIColor.clear
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.extraLight)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.backgroundColor = UIColor.clear
blurEffectView.frame = self.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(blurEffectView)
}
}
The result is quite different from what I expected.
How to customize it to make this blur much lighter, so the picture still shows some details?
As https://stackoverflow.com/users/1387306/chrstpsln mentioned in the comment, this can be achieved by changing the alpha of the UIVisualEffectView.
As a bonus, you can add a fade animation to make the transition feel smoother.
Heres an example of how you can add this effect to any viewController through an extension:
extension UIViewController {
func addBlurEffect() {
let blurEffect = UIBlurEffect(style: .light)
let blurVisualEffectView = UIVisualEffectView(effect: blurEffect)
blurVisualEffectView.frame = UIScreen.main.bounds
blurVisualEffectView.alpha = 0.1
// Bonus animation - Or just set the alpha higher
UIView.animate(withDuration: 0.3) {
blurVisualEffectView.alpha = 0.90
}
view.addSubview(blurVisualEffectView)
}
}
Usage:
let viewController = UIViewController()
viewController.addBlurEffect()
You can't change the blur amount, the only thing you can do is to change the blur effect style:
UIBlurEffectStyle.light
UIBlurEffectStyle.extraLight
UIBlurEffectStyle.dark
Try to change the blur effect alpha. Also, since your image is light, you should use the light style and not the extra light in order to not lose details.
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light)
blurEffect.alpha = 0.95

UIView added as subview to the Application Window does not rotate when the device rotates

I had created an extension to the UIView and created an ActivityIndicatorView and added it as the subview to UIApplication Window. Now when the device rotates the UIViewController also rotates and not this ActivityIndicatorView.
internal extension UIView{
func showActivityViewWithText(text: String?) -> UIView{
let window = UIApplication.sharedApplication().delegate?.window!!
let baseLineView = window!.viewForBaselineLayout()
let locView = UIView(frame:window!.frame)
locView.backgroundColor = UIColor.clearColor()
locView.center = window!.center
baseLineView.addSubview(locView)
baseLineView.bringSubviewToFront(locView)
let overlay = UIView(frame: locView.frame)
overlay.backgroundColor = UIColor.blackColor()
overlay.alpha = 0.35
locView.addSubview(overlay)
locView.bringSubviewToFront(overlay)
let hud = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
hud.hidesWhenStopped = true
hud.center = CGPoint(x: locView.frame.size.width/2,
y: locView.frame.size.height/2)
hud.transform = CGAffineTransformMakeScale(1, 1)
hud.color = UIColor.redColor()
hud.startAnimating()
locView.addSubview(hud)
locView.bringSubviewToFront(hud)
}
May be problem is in missed autoresizing mask? Try to add:
hud.autoresizingMask = [ .flexibleTopMargin, .flexibleBottomMargin, .flexibleLeftMargin, .flexibleRightMargin ]
In a reason your hud is subview of a locView autoresizingMask is required for locView too I suppose.

Resources