I'm trying to develop a drawing application, and I need to change the width of the path according to the speed of the hand.
I tried to use moveToPoint to start another subpath
myPath.moveToPoint(myPath.currentPoint)
myPath.lineWidth = myPath.lineWidth + 1
but it doesn't work, it changes the width of the entire path.
Do you know if there's a way to change only a subpath's width?
That is not possible. All UIBezierPath properties (lineWidth, flatness, ...) apply to the entire path with all its subpaths.
To draw curves with different line widths you have to create multiple bezier paths.
Related
I am offsetting a CGPath using copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:). The problem is the offset path introduces all kinds of jagged lines that seem to be the result of a miter join. Changing the miterLimit to 0 has no effect, and using a bevel line join also makes no difference.
In this image there is the original path (before applying strokingWithWidth), an offset path using miter join, and an offset path using bevel join. Why doesn't using bevel join have any affect?
Code using miter (Note that using CGLineJoin.round produces identical results):
let pathOffset = path.copy(strokingWithWidth: 4.0,
lineCap: CGLineCap.butt,
lineJoin: CGLineJoin.miter,
miterLimit: 20.0)
context.saveGState()
context.setStrokeColor(UIColor.red.cgColor)
context.addPath(pathOffset)
context.strokePath()
context.restoreGState()
Code using bevel:
let pathOffset = path.copy(strokingWithWidth: 4.0,
lineCap: CGLineCap.butt,
lineJoin: CGLineJoin.bevel,
miterLimit: 0.0)
context.saveGState()
context.setStrokeColor(UIColor.red.cgColor)
context.addPath(pathOffset)
context.strokePath()
context.restoreGState()
Here is a path consisting of two line segments:
Here's what it looks like if I stroke it with bevel joins at a line width of 30:
If I make a stroked copy of the path with the same parameters, the stroked copy looks like this:
Notice that triangle in there? That appears because Core Graphics creates the stroked copy in a simple way: it traces along the each segment of the original path, creating a copied segment that is offset by 15 points. It joins each of these copied segments with straight lines (because I specified bevel joins). In slow motion, the copy operation looks like this:
So on the inside of the joint, we get a triangle, and on the outside, we get the flat bevel.
When Core Graphics strokes the original path, that triangle is harmless, because Core Graphics uses the non-zero winding rule to fill the stroke. But when you stroke the stroked copy, the triangle becomes visible.
Now, if I scale down the line width used when I make the stroked copy, the triangle becomes smaller. And if I then increase the line width used to draw the stroked copy, and draw the stroked copy with mitered joins, the triangle can actually end up looking like it's filled in:
Now, suppose I replace that single joint in the original path with two joints connected by a very short line, creating a (very small) flat spot on the bottom:
When I make a stroked copy of this path, the copy has two internal triangles, and if I stroke the stroked copy, it looks like this:
So that's where those weird shapes star shapes come from when you make a stroked copy of your paths: very short segments creating overlapping triangles.
Note that I made my copies with bevel joins. Using miter joins when making the copy also creates the hidden triangles, because the choice of join only affects the outside of the joint, not the inside of the joint.
However, the choice of join does matter when stroking the stroked copy, because the use of miter joins makes the stars larger. See this document for a good illustration of how much the join style can affect the appearance of an acute angle.
So the miter joins make the triangles' points stick out quite far, which makes the overlapping triangles look like a star. Here's the result if I stroke the stroked copy using bevel joins instead:
The star is nigh-invisible here because the triangles are drawn with blunted corners.
If the inner triangles are unacceptable to you, you will have to write your own function (or find one on the Internet) to make a stroked copy of the path without the triangles, or to eliminate the triangles from the copy.
If your path consists entirely of flat segments, the easiest solution is probably to use an existing polygon-clipping library. The “union” operation, applied to the stroked copy, should eliminate the inner triangles. See this answer for example. Note that these libraries tend to be written in C++, so you'll probably have to write some Objective-C++ code since Swift cannot call C++ code directly.
In case you're wondering how I generated the graphics for this answer, I did it using this Swift playground.
I want to draw target view like this-
And get touch event of a particular circle.
e.g If user touch between circle 7 then fill black color of circles up to circles 7.
Currently I have two ways to implement this functionality:
1) Take 10 UIImageView and put on each other and touch of an image view change the colors of image view's according to conditions.
2) Take UIView and draw 20 color gradient (10 for black border line and 10 for white spaces) and save frame of each gradient. After that get user's touch area then change color according to that.
I am looking for a better solution.
why not have a single image, and calculate the radius based on the touch point - all you need to know is the centre position.
Instead of radius, what you really need is an index from 0 to 11 for your bands - if they are equal thickness, you can do that in a single calculation - take the integer part of (11 * radius / radiusFull)
If the bulls-eye is a different size, you may need to add some more code.
Either way, you should be able to do it all with a single image - generated on the fly, or simply loaded - and a bit of simple maths.
I wanted to draw different set of lines in the same loop inside drawRect but with different color.
say set of lines with blue and the other with red alternatively.
initially, I was using two different for loops and and stroking the context twice with different colors.
But then I came across CGMutablePathRef and decided to use two different paths and stroke them with different colors and write code in the same for loop.
Now my problem is that I couldn't find way to set a color for a path.
It appears that we can only set color for Context and not to paths.
I am quite new to iOS and cocoa.
What should I do ? Shall I add the first path to context, set the color, stroke the path and then add the other stroke to the context and set the new color and stroke the context again.
Is there a better,neater and cleaner way for doing this ?
Shall I add the first path to context, set the color, stroke the path and then add the other stroke to the context and set the new color and stroke the context again.
Yes, that's how you do it.
I'm drawing the 'slices' in a custom pie chart in Core Graphics, using a UIBezierPath and the [path addArcWithCenter:radius:startAngle:endAngle:clockwise:] method. My problem is that often the pointy end of the slice actually juts past the center point, intruding into other slices space.
Is there a way to 'round' this edge?
Here is the code im using to draw the path
[path moveToPoint:center];
[path addArcWithCenter:center radius:radius startAngle:interiorAngle endAngle:totalAngle clockwise:YES];
[path addLineToPoint:center];
[path closePath];
Here is an image of the problem:
The white pointy end of the blue piece on the lower left intrudes slightly into the large blue piece in the upper right.
It's not "past" the center point. Your confusion lies in the fact that you're stroking the path. When you stroke a path, the stroke lies centered upon the path, and therefore half of the stroke is outside the path and half of the stroke is inside the path. If you want an accurate stroke, you have two options:
Fill your path with your stroke color, then construct another path that's inset into your first one by the desired line width, and then fill that path with your fill color. This will simulate an "inside" stroke, although it's not usable if your stroke or fill colors are semi-transparent.
Clip to your path, double the stroke width, and then stroke your path. The clipping will force the stroke to only draw inside the path. However, this may not look quite "accurate" at corners (not really sure) since it's doubling the stroke width rather than calculating the "desired" path.
Alternatively, you could try just setting the lineJoinStyle to something other than kCGLineJoinMiter. With the default miter style, the lines actually draw out as far as they need to from the corner in order to meet, which means they can go past 1/2 the line width. If you use kCGLineJoinRound or kCGLineJoineBevel they cannot go past 1/2 the line width. This may not be quite accurate, but it may be good enough for what you want.
I would suspect the problem is with the line width of the white line. For example, a line that is 8 points wide, would have 4 points on either side of the path.
I can't see the problem in that image (which slice is showing it?), but I can suggest that you remove the addLineToPoint: message. It isn't necessary; closePath will return to the center, since that's where you started.
I want to create bezier path with dynamically changing line's width. I need spending the same amount of color on each part of line. So, longer line should be thinner. And shorter line should be bold. Or, at least, line should change it's width from beginning to the end.
Any ideas how to achieve it?
Thank you.
the only way to achieve this is with multiple UIBezierPaths of different thickness and colour.