I have tried lots of things from SO, but I'm not able to make following type of gradient shadow.
Can anyone help to make like this?
I have tried following code.
func addGradientShadowToMakeReservation() {
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [
UIColor(named: "GradiantColor1")!.cgColor,
UIColor(named: "GradiantColor2")!.cgColor,
UIColor(named: "GradiantColor2")!.cgColor
]
gradientLayer.frame = CGRect(x: self.viewReservationGradiant.frame.minX - 40,
y: self.viewReservationGradiant.frame.minY - 40,
width: self.viewReservationGradiant.frame.width + 80,
height: self.viewReservationGradiant.frame.height + 80)
gradientLayer.masksToBounds = true
let shadowLayer = CALayer()
shadowLayer.frame = gradientLayer.bounds
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowOpacity = 0.08
shadowLayer.shadowRadius = 20
shadowLayer.shadowPath = CGPath(rect: shadowLayer.bounds, transform: nil)
gradientLayer.mask = shadowLayer
self.viewReservationGradiant.clipsToBounds = false
self.viewReservationGradiant.layer.addSublayer(gradientLayer)
}
But its didn't help me as expected.
Related
How can I add a dashed border in certain side of the UIView only?Have referred to Dashed line border around UIView link, but I am kind of confused while giving the path for all the edges of a view.ok so, this is my code for adding a straight line border in particular side of a view:
func addRightBorder(with color: UIColor, andWidth borderWidth: CGFloat,view:UIView) {
let border = UIView()
border.backgroundColor = color
border.autoresizingMask = [.flexibleHeight, .flexibleLeftMargin]
border.frame = CGRect(x: view.frame.size.width - borderWidth, y: 0, width: borderWidth, height: view.frame.size.height)
//border.addDashedLine(color: UIColor.red)
border.addDashedLine(color: UIColor.red)
view.addSubview(border)
}*
How can I achieve the same thing but for dashed/dotted lines?
In one of my app, I also needed a similar thing and I created UIView Extension.
Take a look at my code snippet,
extension UIView {
func removeDashedLine() {
_ = layer.sublayers?.filter({ $0.name == "DashedTopLine" }).map({ $0.removeFromSuperlayer() })
}
func addDashedLine(_ path: UIBezierPath ,pattern : [NSNumber] = [1, 5], color: UIColor = UIColor.red) {
self.backgroundColor = UIColor.clear
let shapeLayer: CAShapeLayer = CAShapeLayer()
let frameSize = self.frame.size
let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
shapeLayer.name = "DashedTopLine"
shapeLayer.frame = shapeRect
//shapeLayer.position = CGPoint(x: frameSize.width / 2, y: frameSize.height / 2)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = 1
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineDashPattern = pattern
shapeLayer.path = nil
shapeLayer.path = path.cgPath
self.layer.insertSublayer(shapeLayer, at: 0)
}
}
It may not do exactly what you want but should help you
I have been exploring this questions through playgrounds for days now without finding any solution.
I am been managing to create a hole in a blurred UIView. Now I am wondering how to animate the radius of this circle, ergo animating the blurred view's mask's path.
Here is my code so far:
import Foundation
import UIKit
import PlaygroundSupport
let contentRect = CGRect(x: 0, y: 0, width: 400, height: 400)
let contentView = UIView(frame: contentRect)
contentView.backgroundColor = .white
PlaygroundPage.current.liveView = contentView
let image = UIImage(named: "landscape.jpg")
let imageView = UIImageView(frame: contentRect, image: image)
imageView.contentMode = .scaleAspectFill
let blur = UIBlurEffect(style: .light)
let blurView = UIVisualEffectView(effect: blur)
blurView.frame = contentRect
let path = UIBezierPath(rect: contentRect)
let circle = UIBezierPath(ovalIn: CGRect(x: 50, y: 50, width: 300, height: 300))
path.append(circle)
path.usesEvenOddFillRule = true
let toPath = UIBezierPath(rect: contentRect)
let toCircle = UIBezierPath(ovalIn: CGRect(x: 150, y: 150, width: 100, height: 100))
toPath.append(toCircle)
toPath.usesEvenOddFillRule = true
let hole = CAShapeLayer()
hole.path = path.cgPath
hole.fillColor = UIColor.green.cgColor
hole.fillRule = kCAFillRuleEvenOdd
let mask = UIView(frame: contentRect)
contentView.addSubview(imageView)
contentView.addSubview(blurView)
mask.layer.addSublayer(hole)
blurView.mask = mask
I have been trying this, without success:
let animation = CABasicAnimation(keyPath: "path")
animation.fromValue = hole.path
animation.toValue = toPath.cgPath
animation.duration = 2
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
hole.add(animation, forKey: nil)
CATransaction.begin()
CATransaction.disableActions()
hole.path = toPath.cgPath
CATransaction.commit()
Also tried UIView.animate(... or extracting blurView.layer.sublayers to add them to my contentView, but it just gets messy.
Another approach would be to use a CGAffineTransform, which I tried, but the blur gets disrupted. Plus I have trouble to use a workaround when I feel so close to a solution.
Any help would be greatly appreciated! Thanks!
I've make a path in order to mask my view:
let path = // create magic path (uiview bounds + 2 arcs)
let mask = CAShapeLayer()
mask.path = path.cgPath
view.layer.masksToBounds = false
view.layer.mask = mask
Up to here all ok.
Now I would like to add a shadow that follows path, is it possibile?
I try in several way, the last one is:
mask.shadowPath = path.cgPath
mask.shadowColor = UIColor.red.cgColor
mask.shadowOffset = CGSize(width: 10, height: 2.0)
mask.shadowOpacity = 0.5
But this produce a partial shadow and with color of the original view..
With debug view hierarchy:
Any advice?
Final result should be similar to this, but with shadow that "follows" arcs on path.
When you add a mask to a layer, it clips anything outside that mask - including the shadow. To achieve this you'll need to add a "shadow" view below your masked view, that has the same path as the mask.
Or add a shadow layer to the masked view's superview.
let view = UIView(frame: CGRect(x: 50, y: 70, width: 100, height: 60))
view.backgroundColor = .cyan
let mask = CAShapeLayer()
mask.path = UIBezierPath(roundedRect: view.bounds, cornerRadius: 10).cgPath
view.layer.mask = mask
let shadowLayer = CAShapeLayer()
shadowLayer.frame = view.frame
shadowLayer.path = UIBezierPath(roundedRect: view.bounds, cornerRadius: 10).cgPath
shadowLayer.shadowOpacity = 0.5
shadowLayer.shadowRadius = 5
shadowLayer.masksToBounds = false
shadowLayer.shadowOffset = .zero
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
container.backgroundColor = .white
container.layer.addSublayer(shadowLayer)
container.addSubview(view)
If you're going to be using this elsewhere, you could create a ShadowMaskedView that contains the shadow layer, and the masked view - maybe with a path property.
You can try this extension:
extension UIView {
func dropShadow() {
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowOffset = CGSize(width: -1, height: 1)
self.layer.shadowRadius = 1
self.layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
self.layer.shouldRasterize = true
}
}
I'm making a custom view that i want to mask and to add shadow to it.
the masking:
let p = UIBezierPath()
p.moveToPoint(CGPointMake(20, 20))
p.addLineToPoint(CGPointMake(100, 20))
p.addLineToPoint(CGPointMake(100, 50))
p.addLineToPoint(CGPointMake(110, 55))
p.addLineToPoint(CGPointMake(100, 60))
p.addLineToPoint(CGPointMake(100, 100))
p.addLineToPoint(CGPointMake(20, 100))
p.closePath()
let s = CAShapeLayer()
s.frame = layer.bounds
s.path = p.CGPath
s.fillColor = UIColor.greenColor().CGColor
layer.mask = s
the masking works, now i want to add a shadow.
but its not working.
i tried to add shadow to the main layer and nothing happens.
layer.shadowColor = UIColor.yellowColor().CGColor
layer.shadowRadius = 10
layer.shadowOpacity = 0.9
layer.shadowOffset = CGSizeZero
i tried to add it to the mask layer and i got the main view masked with a shadow.
s.shadowColor = UIColor.yellowColor().CGColor
s.shadowRadius = 10
s.shadowOpacity = 0.9
s.shadowOffset = CGSizeZero
Any suggestions how to add this yellow shadow to the masked view?
Thanks
Thanks #WilsonXJ
I changed mask to addSubLayer.
This is the answer that worked for me:
let p = UIBezierPath()
p.moveToPoint(CGPointMake(20, 20))
p.addLineToPoint(CGPointMake(100, 20))
p.addLineToPoint(CGPointMake(100, 50))
p.addLineToPoint(CGPointMake(110, 55))
p.addLineToPoint(CGPointMake(100, 60))
p.addLineToPoint(CGPointMake(100, 100))
p.addLineToPoint(CGPointMake(20, 100))
p.closePath()
let s = CAShapeLayer()
s.fillColor = UIColor.whiteColor().CGColor
s.frame = layer.bounds
s.path = p.CGPath
layer.backgroundColor = UIColor.clearColor().CGColor
layer.addSublayer(s)
layer.masksToBounds = true
layer.shadowColor = UIColor.yellowColor().CGColor
layer.shadowOffset = CGSizeZero
layer.shadowOpacity = 0.9
layer.shadowPath = p.CGPath
layer.shadowRadius = 10
I don't think that current answer is the right one, because there is no layer.mask usage anymore.
In case when you need to use layer.mask and to drop shadow of masked layer - the obvious solution is to add another layer below masked layer that will have same shape as layer.mask and drop it's shadow
example:
let view = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 500, height: 500)))
view.backgroundColor = .white
PlaygroundPage.current.liveView = view
let path: CGPath = ...
let maskedView = UIView(frame: path.boundingBox)
maskedView.center = view.center
maskedView.backgroundColor = .green
view.addSubview(maskedView)
let maskLayer = CAShapeLayer()
maskLayer.frame = maskedView.bounds
maskLayer.path = path
maskedView.layer.mask = maskLayer
let shadowLayer = CAShapeLayer()
shadowLayer.path = path
shadowLayer.frame = maskedView.frame
shadowLayer.shadowOpacity = 0.4
shadowLayer.shadowRadius = 2
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowOffset = CGSize(width: 4, height: 4)
maskedView.superview!.layer.insertSublayer(shadowLayer, below: maskedView.layer)
I'm having trouble aligning a UIView and a CAShapeLayer.
Here is sample code demonstrating the issue:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let square = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
square.transform = CGAffineTransformMakeRotation(CGFloat(20*M_PI/180))
square.backgroundColor = UIColor.grayColor()
view.addSubview(square)
let strokeLayer = CAShapeLayer()
strokeLayer.path = UIBezierPath(rect: square.bounds).CGPath
strokeLayer.position = square.center
strokeLayer.setAffineTransform(square.transform)
strokeLayer.fillColor = UIColor.clearColor().CGColor
strokeLayer.strokeColor = UIColor.redColor().CGColor
strokeLayer.lineWidth = 1
view.addSubview(square)
view.layer.addSublayer(strokeLayer)
}
}
Here's an image of the result:
How do I get the two to align?
The only change you would need to make is to add:
strokeLayer.frame = square.layer.bounds
Right after:
let strokeLayer = CAShapeLayer()
I.e.,
let strokeLayer = CAShapeLayer()
strokeLayer.frame = square.layer.bounds
strokeLayer.path = UIBezierPath(rect: square.bounds).CGPath
strokeLayer.position = square.center
strokeLayer.setAffineTransform(square.transform)
strokeLayer.fillColor = UIColor.clearColor().CGColor
strokeLayer.strokeColor = UIColor.redColor().CGColor
strokeLayer.lineWidth = 1
What if you position your shape layer at (0,0)
strokeLayer.position = CGPointMake( 0, 0 )