Update 02 has a working solution...
I am trying to use the stroke of a UIBezierPath as a mask on another UIView. There are many examples, but they all use the fill to clip views.
I'm trying to use only the stroke of the path, but it's not displaying correctly.
Below is what I currently have in Swift 3.x:
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: 100, y: 100))
bezierPath.addLine(to: CGPoint(x: 200, y: 50))
bezierPath.addLine(to: CGPoint(x: 300, y: 100))
bezierPath.lineWidth = 5.0
bezierPath.stroke()
let gradient = Gradient(frame: self.bounds)
let mask = CAShapeLayer()
mask.path = bezierPath.cgPath
gradient.layer.mask = mask
self.addSubview(gradient)
But, the above code does this. I'm looking for only to use the stroke for the mask... This is what the code is currently doing
This is the desired outcome..
(has more points in this comp snapshot)
I realize that there might be a better way, open to any ideas or alternatives!
--Update 01--
My latest, but masks out everything:
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: 100, y: 100))
bezierPath.addLine(to: CGPoint(x: 200, y: 50))
bezierPath.addLine(to: CGPoint(x: 300, y: 100))
bezierPath.lineWidth = 5.0
bezierPath.stroke()
let gradient = Gradient(frame: self.bounds)
UIGraphicsBeginImageContext(CGSize(width: gradient.bounds.width, height: gradient.bounds.height))
let context : CGContext = UIGraphicsGetCurrentContext()!
context.addPath(bezierPath.cgPath)
let image = UIGraphicsGetImageFromCurrentImageContext()
gradient.layer.mask?.contents = image?.cgImage
And.. got nowhere after trying to figure it out with CAShapeLayer:
let mask = CAShapeLayer()
mask.fillColor = UIColor.black.cgColor
mask.strokeColor = UIColor.black.cgColor
mask.fillRule = kCAFillModeBoth
mask.path = bezierPath.cgPath
gradient.layer.mask = mask
self.addSubview(gradient)
--Update 02 - Working Solution --
Thanks for everybody's help!
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 0, y: 0))
bezierPath.addLine(to: CGPoint(x: 100, y: 300))
bezierPath.addLine(to: CGPoint(x: 200, y: 50))
bezierPath.addLine(to: CGPoint(x: 300, y: 200))
bezierPath.addLine(to: CGPoint(x: 400, y: 100))
bezierPath.addLine(to: CGPoint(x: 500, y: 200))
let gradient = Gradient(frame: self.bounds) // subclass of UIView
let mask = CAShapeLayer()
mask.fillColor = nil
mask.strokeColor = UIColor.black.cgColor
mask.path = bezierPath.cgPath
mask.lineWidth = 5.0
gradient.layer.mask = mask
self.addSubview(gradient)
And, the results are what I was wanting:
A UIBezierPath
has several properties that only matter when stroking the path, including lineWidth
, lineCapStyle
, lineJoinStyle
, and miterLimit
.
A CGPath
has none of these properties.
Thus when you set mask.path = bezierPath.cgPath
, none of the stroke properties you set on bezierPath
carries over. You've extracted just the CGPath
from the UIBezierPath
and none of those other properties.
You need to set the stroking properties on the CAShapeLayer
rather than on any path object. Thus:
mask.path = bezierPath.cgPath
mask.lineWidth = 5
You also need to set a stroke color, because the default stroke color is nil, which means don't stroke at all:
mask.strokeColor = UIColor.white.cgColor
And, because you don't want the shape layer to fill the path, you need to set the fill color to nil:
mask.fillColor = nil
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With