Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
596 views
in Technique[技术] by (71.8m points)

ios - Mask UIView with UIBezierPath - Stroke Only

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

Currently doing..

This is the desired outcome.. 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:

Expected Result

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...