I am working on drawing app. I want to implement eraser functionality that erases the path drawn using UIBezierPath.Is it possible??
I have tried below code but its not working
UIGraphicsBeginImageContextWithOptions(self.tempImageView.frame.size, false, 0.0)
if let context = UIGraphicsGetCurrentContext()
{
tempImageView.image?.draw(in: CGRect(x:
0, y: 0, width: self.tempImageView.frame.size.width, height: self.tempImageView.frame.size.height))
let sublayer = CAShapeLayer()
self.tempImageView.layer.addSublayer(sublayer)
context.setBlendMode(CGBlendMode.clear)
sublayer.strokeColor = editorPanelView.drawingColor.cgColor
sublayer.lineJoin = kCALineJoinRound
sublayer.lineCap = kCALineCapRound
sublayer.lineWidth = editorPanelView.brushWidth
let path = UIBezierPath()
path.move(to: CGPoint(x: mid1.x, y: mid1.y))
path.addQuadCurve(to:CGPoint(x:mid2.x, y:mid2.y) , controlPoint: CGPoint(x: prevPoint1.x, y: prevPoint1.y))
sublayer.path = path.cgPath
layerArray.append(sublayer)
UIGraphicsEndImageContext()
}
I am not able to find any solution for this. Any help (even if it's just a kick in the right direction) would be appreciated.
A CAShapeLayer is a vector drawing. It's shapes. The usual eraser tool works with raster (pixel-based) drawings. The two are different, and there isn't a straightforward way to do an eraser tool on vector drawings.
One way you could do it is to have the eraser tool edit a mask layer which is applied to your shape layer. It would have the effect of erasing the parts of the shape layer where the mask is opaque.
One tricky part to this is that once you've drawn into the mask, you'd have to erase that part of the mask in order for the user to be able to draw new content into the erased area, and that might cause the erased parts of the old drawing to show through.
Getting what you want to do to work well would be quite tricky.
Related
hi i have a circle shapeLayer need color by GradientColor
i tried ues CAGradientLayer and mask it like this
let gradientChargeLayer = CAGradientLayer()
gradientChargeLayer.colors = CGColor.mColor
gradientChargeLayer.frame = vShape.bounds
gradientChargeLayer.locations = [0,1]
gradientChargeLayer.startPoint = CGPoint(x: 0,y: 0.5)
gradientChargeLayer.endPoint = CGPoint(x: 1,y: 0.5)
gradientChargeLayer.mask = shapeLayer
vShape.layer.addSublayer(gradientChargeLayer)
but what i want is gradient color from stroke start to end
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor <<< here
thanks
you could’ve different approaches I believe
One, you could make an outer and inner circle and then add the required path/layer to the outer circle (this circle bigger than inner) both circles without visible stroke
Or I could recommend you UIGraphigsImageRenderer to work with
https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer
This last have a context that could be used to do more advanced rendering context.cgContext.setStrokePattern here you can add a CGPattern and colors to trying accomplish it
PD. Sorry if my english is confuse
Shape layers only draw in a single color, as I guess you are aware.
By making your shape layer a mask onto a gradient layer you're able to have the shape layer reveal the color of the underlying gradient layer.
If you are always drawing a circle and want the color of the circle's stroke to vary from beginning to end you could set up your gradient layer using a type of conic, and specifying the colors in the gradient layer to begin and end where you want them. However, that would only work for shape layers that contain ovals, where the size and center of the shape and the size and the center of the gradient match.
As #Guillodacosta said in their answer, you may have to give up on using a shape layer and draw your graphic into a graphics context using Core Graphics. (You could do that once and capture the results into a UIImageView to avoid the slowdown of Core Graphics rendering.)
I can't understand why the "strokeTexture" option isn't working on a SKShapeNode. Or, how can I do border with texture? Thanks in advance!
let shape = SKShapeNode(circleOfRadius: 100)
shape.position = CGPoint(x: 200, y: 400)
shape.fillColor = SKColor.white
shape.fillTexture = SKTexture(imageNamed: "test")
shape.lineWidth = 50
shape.strokeColor = SKColor.white
shape.strokeTexture = SKTexture(imageNamed: "test(5)")
Output:
Test image:
Test(5) image:
It doesn't work in Simulator!
Try it on your device.
I get the feeling this stroke might be being done with metal, whereas the fill is somehow not being done with metal, so is visible in the simulator.
The fill also doesn't rotate with the object on the device, but the stroke/outline and its texture do.
This would tend to indicate that an SKShapeNode without a fill might have reasonable performance.
Set the width and height of the texture, that is used to render the stroke, multiple of 8 pixels. In your case Test(5) has dimensions 100x100. If you change this texture for another one with dimensions, e.g. 96x96 pixels, the stroke will be rendered correctly and displayed. I don't know, why there are no reference to this fact in the official documentation.
Hi i have a question about shapes and animation of the shapes.
I would like to understand what is the best practice to create these kind of shapes (shapes that are presented on images around buttons).
Previous investigation shows that usage of the UIBezierPathis required but I am interest in how to create this kind of path and constantly animate it? Or probably should i use SpriteKit for that?
I think Spritekit is not necessary. You make this kind of animation just by acting on the CALayer masks.
If you writing a series of masks based each time on a different ellipse ( and build an animations based on it), you can re-produce this effect in your button.
Some useful code examples:
var shape = CAShapeLayer()
shape.fillColor = UIColor(white: 0.90, alpha: 1).CGColor
var image = UIImage(named: "some_image")
shape.contents = image?.CGImage
var ovalPath = UIBezierPath(ovalInRect: CGRectMake(160, 160, 240, 320))
I have a custom class that manages a custom view that has a horizontal center on screen (which will represent a solid line). And I've drawn the line like this:
override func drawRect(rect: CGRect)
{
let line = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(line, 3.0)
CGContextSetStrokeColorWithColor(line, UIColor.redColor().CGColor)
CGContextMoveToPoint(line, 0, self.bounds.midY)
CGContextAddLineToPoint(line, self.bounds.width, self.bounds.midY)
CGContextStrokePath(line)
}
However, I need to draw a multiple solid circles on this line and try to look it like a point in a chart. I'm trying to make a mini chart representation in fact. How can I draw the circles? With a nested 'for in' loop or? Is there any official chart API from Apple?
UIGraphicsGetCurrentContext() gives you a context that you can draw multiple things into. Calling it line is not the right idea, because it can contain multiple lines, or circles, or all sorts of other things.
Think of the context as an artist sitting in front of a blank canvas. You give the artist instructions, like "draw a red line, then draw a blue circle". The artist follows the instructions, and afterwards, you look at the canvas.
Here's how you might draw a line and then a circle.
let context = UIGraphicsGetCurrentContext()
// Tell the context what stroked paths should look like
CGContextSetLineWidth(context, 3.0)
CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor)
// Draw a single line
CGContextMoveToPoint(context, 0, self.bounds.midY)
CGContextAddLineToPoint(context, self.bounds.width, self.bounds.midY)
CGContextStrokePath(context)
// Now draw a circle by filling a path.
// First, set the fill color:
CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor)
// Specify how big the circle is, and where its center is:
let circleRadius = CGFloat(5.0)
let circleCenter = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
// Then add a circle to the context, by specifying the rectangle that surrounds it:
let circleRect = CGRect(x: circleCenter.x - circleRadius,
y: circleCenter.y - circleRadius,
width: circleRadius * 2,
height: circleRadius * 2)
CGContextAddEllipseInRect(context, circleRect)
// And fill that circle:
CGContextFillPath(context)
If you want to draw more circles but in different places, just call CGContextAddEllipseInRect and CGContextFillPath again, but with different values for circleRect. Depending on what you want, a for loop might be appropriate. It's entirely up to you.
If you don't want to write it yourself, there are lots of 3rd-party chart libraries available, just do a search. Apple does not provide an "official" one.
Any suggestion on using SpriteKit rendering engine, but have the ability to draw game graphic programmatically. Those graphics are simple but slightly complex than just rectangular and oval.
I recently found out the SKShapeNode have a cap on the complexity when using custom path. I basically have a function that take a number to generate random CGPath shape, The larger the number the more complex and details about the shape. When I use a small number, there is no problem, but when I use a larger number to generate more complex path, SKShapeNode give me this error. (I tested on real device with iOS9.2.1 as well)
Assertion failed: (length + offset <= _length)
Related Question
Now I don't think using SKShapeNode is a good idea, so any suggestion on programmatically draw graphics that able to work nicely with SpriteKit?
I read somewhere else which is draw using the any graphics API available as long as it can convert to a format that SKTexture can initialize, is the right way to go?
If you will use SKTexture you can draw your chart with CGContext.
First of all you need create array of chart's points like:
let pathPoints: [CGPoint] = [...]
Then you need to call UIGraphicsBeginImageContext(rect.size) where rect.size is CGSize object which determines the size of the region in which will be inscribed the chart and rect is CGRect object. Next step is creation of CGContextRef and it setting:
UIGraphicsBeginImageContext(size)
let context: CGContextRef = UIGraphicsGetCurrentContext()!
CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor);
CGContextSetLineWidth(context, lineWidth)
where lineWidth is CGFloat type value.
After this you can create your chart in context like this:
if pathPoints.count > 1 {
CGContextMoveToPoint(context, pathPoints.first!.x, pathPoints.first!.y)
for var i = 1; i < pathPoints.count; ++i {
CGContextAddLineToPoint(context, pathPoints[i].x, pathPoints[i].y)
}
}
CGContextStrokePath(context)
Then you need to create image form context like this:
let image = UIGraphicsGetImageFromCurrentImageContext()
Then create texture form image and use it for SKSpriteNode object that will be added to the scene:
let pathTexture = SKTexture(image: image)
let pathNode = SKSpriteNode(texture: pathTexture)
pathNode.position = CGPointMake(rect.origin.x + rect.width/2 - lineWidth/2, rect.origin.y + rect.height/2 - lineWidth/2)
pathNode.zPosition = 0
someParrentNodeThatOnScene.addChild(pathNode)
UIGraphicsEndImageContext()
That's all you need to create chart.