When I use CAShapeLayer and create a rectangle shape then the path starts at rectangle's origin (top-left corner) and draws clockwise. Now if I want to draw only part of the shape then I'm using strokeStart and strokeEnd properties. The problem comes when I want to draw a part consisting the path's end points. In this case the path is closed and it starts and ends on rectangle's top-left corner. When I'm setting strokeStart=0.8 and strokeEnd=0.2 I'm hoping it to draw the last section of the path and a bit from the beginning of the path. However, this is not working. Are there any ideas or tricks how to do this?
Update:
Adding an image to clarify what I mean above. I want an animation which draws a small amount of rectangle and that drawn part circles over the rectangle:
The short answer is that I don't think you can do that, at least not with a single path that will draw any of the segments in your examples. I'm pretty sure that strokeStart must be less than strokeEnd.
If you want to draw your last segment you'd need to create a custom rectangle path that started at the lower left corner and wrapped around.
Related
Constructing custom variations of rounded rectangles is fussy business. The Swift V 5.1 answer in this thread:
Swift: UIBezierPath Stroke Animation from Center
Describes out to draw a rounded rectangle starting from the top center, so you can animate stroking it starting from that point.
It uses the UIBezierPath method addArc(withCenter:radius:startAngle:endAngle:clockwise:) to draw the corner arcs of the rounded rectangle.
Getting the code right to draw the connected series of line segments and quarter arcs requires quite a bit of care, plus some knowledge of trig to figure out the angles of the different arcs for each quadrant.
CGPath has a variation on addArc, addArc(tangent1End:tangent2End:radius:transform:), that works differently. It takes "tangent end" points that define the arc in a way that's less fussy to set up (once you figure out how it works.)
I can't find an equivalent method for UIBezierPath. Am I missing something? I guess the answer is to create a CGPath (or rather, CGMutablePath using calls to addArc(tangent1End:tangent2End:radius:transform:) and then use the UIBezierPath initializer that takes a CGPath, but that is an added step that makes the code a little bit more confusing.
It looks like there is no UIBezierPath equivalent to the CGPath method addArc(tangent1End:tangent2End:radius:transform:).
I guess the answer is to just use CGPath when you want to add arcs in the form addArc(tangent1End:tangent2End:radius:transform:), and then convert the result to a UIBezierPath using the UIBezierPath.init(cgPath:) initializer.
At first I was thinking I could write an extension of UIBezierPath that would add a addArc(tangent1End:tangent2End:radius:transform:) method to UIBezierPath, but that would get very messy, since addArc(tangent1End:tangent2End:radius:transform:) uses the current pen position in the active path to figure out the first tangent line.
I have the following problem: I'm scanning a QR Code with AVFoundation. This works quite well and I can also create a border around the code by adding a subview and set the frame attribute by subview.frame = qrCodeObject.bounds. This only has the problem that the border is only a rectangle and dismisses the perspective of the QR code.
I know that the qrCodeObject has a property corners which incorporates the top right, top left, bottom right and bottom left points of the QR code detected.
My question is now: how can I apply those corner points to the "border" view to make this border to have the same perspective as the QR code? Or in other words: how to "transform" the view according to the corner points?
Thanks a lot in advance!
UPDATE:
Here you can see the problem: the red box is a UIView, having it's frame property set to the QR codes bounds property. This misses perspective. I would like to transform the UIView (the red box) to following the corners property of the QR code, which includes the top right, top left, bottom right and bottom left points (CGPoint) of the QR code. It is important to apply this to a UIView, because I later want to apply it to an ImageView. Also a mask is not usable, as it just hides part of the view, but does not stretch or transform the content of the view.
I found a solution: AGGeometryKit did the trick: https://github.com/hfossli/AGGeometryKit/
Thanks everybody for helping!
You can't transform a CGRect that way, as far as I know. (At least I'm unaware of any framework that can do that kind of image processing.)
What you can do is to draw a polygon using the points of the qrCodeObject.
In drawRect of your UIView, change use CGContext and CGPath to draw the path you'd like.
You want your drawing UIView to be the same size as the one showing the QR code so that you don't have to translate the points onto a second coordinate space.
This answer has directions if you need more guidance on how to do that.
Ok, the problem you are facing is that a CGRect can only represent a rectangle that is not tilted or distorted. What you are dealing with is an image that has different kinds of perspective distortion.
I haven't tried to do this, but it sounds like AVFoundation gives you 4 CGPoint objects for a reason. You need to draw those 4 CGPoints using a UIBezierPath rather than trying to draw a CGRect. Simply create a bezier path that moves to the first point, then draws lines to each subsequent point, and finally, back to the first point. That will give you a quadrilateral that takes into account the distortion of your QR code.
CATransform3DRotate could be your friend, here.
https://guides.codepath.com/ios/Using-Perspective-Transforms might be a good starting point.
I have a UIImageView displaing an image. This view's layer is masked with CAShapeLayer in order to create circular "hole" in the image. To create the hole I use UIBezierPath with .usesEvenOddFillRule = true.
It works fine when static. But I need that hole to move with user finger. To do that I create new UIBezierPath with even-odd rule each time user moves their finger. On smaller phones with smaller images it looks OK but on iPhone 6 Plus it is choppy.
Any ideas on how to make it smooth are very wellcome. I cannot just move the frame of masking CAShapeLayer - it would move the hole bot also hide some edges of the image. So the only way is to change its .path each time user moves finger and that is slow.
EDIT: matt's answer would work in some scenarios but not in my case: I am not displaying the whole image only a part of it defined by UIBezierPath. This part is most often oval (but can be rectangular or rounded rectangle) and it has "hole" cut in it. While the hole is mowing with users finger the displayed part/shape of the image does not change - it is static.
The ineficient solution that was in place so far wa:
Create UIBezierPath with boundary of displayed part of the image
Set 'even off fill rule' on it
Add UIBezierPath of the hole to it
Set it as path of CAShapeLayer with some opaque fill color
Use that CAShapeLayer as mask of the UIImageView
This procedure was repeated each time a user moved their finger. I cannot simply move the whole mask layer as that would also change the part of the image being displayed. I what it to stay static and move only the hole in it.
it would move the hole bot also hide some edges of the image
Well, I don't agree. Moving the mask is exactly the way to do this. I don't see why you think there's a problem with that. Perhaps the issue is merely that you have not made the mask layer big enough. It does not have to be the same size as the layer it is masking. In this case, it needs to be about 9 times the size of the masked layer (3 horizontal and 3 vertical), so that it will continue to cover the masked the layer no matter how far in any direction the user slides it.
If I define an open UIBezierPath and set it as a collision boundary:
_containerPath = [UIBezierPath bezierPathWithArcCenter:center
radius:radius
startAngle:M_PI
endAngle:0
clockwise:NO];
[_collisionBehavior addBoundaryWithIdentifier:#"containerBoundary" forPath:_containerPath];
and then turn gravity on, objects that are released inside the "bowl" respect the lower boundary, but objects released from above the bowl come to rest on the supposedly non-existent side. Is this expected behavior?
In the picture, the red rectangle was dropped from above; the reference view for the dynamic animator is the light gray rect. It fell from above and stopped at the invisible line.
I've confirmed that if you flip the bezier path over, the red rect does in fact respect the curved boundary; I've also tried this using an open (two-sided) triangle instead of curved path - same result.
The behavior you're seeing seems to be the same as what you see for fill with a bezier path. If you draw a "V" and fill it, it behaves as if it were a closed path. With the collision boundaries, you can make an open "V" by adding two lines with addBoundaryWithIdentifier:fromPoint:toPoint:. I don't know it there's any other way around the problem. For your half circle, I presume you could approximate it with a series of straight lines added with the method above. I've approximated circles before using 50 to 100 lines that look very close to what you get with BezierPathWithOvalInRect. I don't know if this creates a serious burden on the system when used as a collision boundary.
I'm trying to use paint code to draw a roundrect with different corner radii. I have nearly everything working by drawing two circles and two roundrects. The problem is I can't make one of the roundrects draw at x offset circle radius have width of "frame.width - circle_radius" - the end effect being it keeps aligned to the right hand edge of the frame.
It feels like I should be able to write frame.width - largeCornerRadius in an expression editor but PaintCode objects to the frame reference.
That said, i's beginning to feels like I could write this code quicker by hand :-)
I don't have an answer to my specific stated question but I have discovered a better way to draw my roundrect as four different rects and turning off the roundrects on the "inner corners:
As you'd expect the drawing code is much better and it resizes well with the enclosing frame.
You can also :
use a set of rect, oval or other shapes
select them and "union" to get one bezier curve for the whole shape
selection each point (or a set of points) of the resulting bezier and fix springs on each of them as fixed or fluid from the edge of the surrounding frame.
I see this question is old, but let me show how to achieve this using Springs & Struts.
Let’s use 2 circles and 2 rounded rectangle, each having only one corner rounded, just like you have. Once you draw a Frame around these shapes, their Springs & Struct inspector becomes enabled.
Here you can click each of 6 segments to toggle fixed or flexible dimension for each shape. For Red Circle, make flexible only top and right margin (just like on the image above) and for Blue Circle the opposite margins (bottom and left). Then for both rectangles make flexible size and fixed margins.
For more information, check out our videos, blog, and documentation on this topic.
– PaintCode Support