Any simple way to "resize" an NSBezierPath? - objective-c

I have an NSBezierPath that I'm filling and stroking. I'd like to add some inner glow to the path (a light stroke, just inside of the outer stroke), and the thing that comes to mind is to use the same path shrunk by 1 pixel (the size of the line that is already )stroked. Is there a way to do this?
Alternatively, is there some sort of pattern I can use when applying both a border (stroke) and a glow to a bezier path?
Example, the (extremely subtle) inner glow on the Google Chrome tabs:

You can resize a NSBezierPath quite easily using an NSAffineTransform.

You know what, you actually DON'T have to resize the bezier...all you have to do is change the stroke width:
make a duplicate of the bezier
on the new one, make the stroke width narrower. (So, maybe 30 on your original, try 26 on this duplicate.)
Put the new smaller one on top of the larger one.
I hope that works for you (hopefully I understood what you were getting at).

Related

Recreate Apple Watch fitness tracker ‘progress’ bar - gradient on CAShapeLayer stroke

I'm writing an app that could make good use of the Apple Watch's fitness tracker design, here:
So far, I've created the basic outline which is just a CAShapeLayer with a CGPath of an ellipse. I use strokeStart and strokeEnd to animate the progress. My problem comes when applying a gradient to the outline. How do I apply a gradient like above to the stroke of a CGPath?
The cleanest way to do this without having to drop down to Core Graphics or GL is to create a layer containing the angle gradient that you want the ring filled with, mask it with a CAShapeLayer containing your circular path (with the appropriate line width and cap settings), then, as you’re currently doing, use the shape layer’s strokeEnd property to set the “fill” percentage. Note that there isn’t a built-in way to create an angle gradient—you can use one of the suggestions in this answer for that.
edit: Also, you’ll need a pair of semicircular “cap” images, one at each end of the ring—as the fill percentage gets close to 100%, the region at the top will reveal the discontinuity between the start and end color. In your example image above, you’d need a red semicircle oriented like this ( at the start, and a pink one oriented like this ) with a translation/rotation transform tracking the end.
additional edit: Also also, since the end-cap semicircle will be moving along the gradient, you’ll need it to change color, interpolating from the start color to the end color as the fill amount goes from 0% to 100%. Best way to do that is with a shape layer with a semicircular path, since you can set the fillColor of that without having to redraw image contents.
We did this for an iOS app.. but quickly stopped as it gets bogged down quickly.
I think Apple is using images.. as they do in the Lister example

Animating arbitrarily-shaped CGPath to rectangle

I am using a CAShapeLayer to display a path that is updated as the user traces his/her finger on the screen. I'd like to transform this path to a rectangle that just encloses the path. I can compute the rectangle just fine; the tricky part is animating the transformation.
The docs say this about animating the path property of CAShapeLayer:
If the two paths have a different number of control points or segments the results are undefined.
So how do I go about adding more control points to the rectangular CGPath? Or is there a better way to achieve this animation? Thanks. =)
I think you have to manually construct a series of lines in the form of a rectangle. Iterate over the elements in the path, compute where on the bounding rectangle you want that point to transform to, and add that segment to the new rectangular path. You may have to use the same kind of element (e.g. cubic or quadratic Bézier curve that happens to form a straight line) so that the number of control points matches.
After the animation completes, you can reset the path to a pure rectangle if you want.

Merge two UIBezierPath points

I want to be able to select some area from this image, and change the color of the selected area.
To do this, I thought of using CALayer and UIBezierPath.
I've cleared the colored area from the image, then I took each area point and drew a UIBezierPath beneath the image.
I have 3 CALayers for each area, each CALayer has a UIBezierPath with predefined points.
When the user clicks on a layer, it will show the selected layer without filling the UIBezierPath, just to have a border around the UIBezierPath, the result look like this:
I added a UIView over the image with Opacity = 0.6f and
redrew all the CALayers on it.
All the layers are hidden in the new UIView
Every thing is working great, the next step is to merge the selected areas:
I took the points from the first area and added it to the points of
the second area
I created a new UIBezierPath with the new points
My problem is that the result is wrong:
How to merge a UIBezierPath with a correct points order?
Is there a better way to accomplish something like this without
using UIBezierPath?
from looking at the image above, the path is wrong because the sequence of points is not followed which pretty much screws up your path. I don't think a Bezier Path is the right tool to do this in the first place as you have rectangular or point to point connections. So you more have a Poligon than a Bezier Path object. However UIKit seems to bundle all this into a UIBezierPath object (non optimal naming if you ask me).
The tricky thing here is to find out where the two shapes really touch each other and to add the points in the sequence as before but then tear up the vertical lines in the middle and connect the path to the other structure.
Another alternative could be to use a bitmap and simply union the bitmaps and create a new shape. It largely depends on how your base data is represented and managed. You could also simply keep two shapes and just join them in a meta object to draw them concurrently.

How to add a shadow to an UIImageView which fits the shape of the image content but with some rotation and shift effect

I have been looking for the solution on the web for a long time. Most tutorials are fairly simple about adding shadow to a UIView. I also noticed that if we add a shadow to an UIImageView. The shadow shape could perfectly fit the shape of the content image if the image itself has alpha channel in it. Say for example, if the image is an animal with transparent background, the shadow shape is also the same as that animal (not a rectangle shadow as same as UIImageView frame).
But these are not enough. What I need to do is to add some changes to the shadow so it may have some rotation angle and compressed (squeezed or shift) effect so that looks like the sunlight comes from a certain spot.
To demonstrate what I need, I upload 2 images below, which I captured from the Google Map App created by Apple. You can imagine the Annotation Pin is an image which has the Pin shape, so the shadow is also "pin shaped", but it is not simply "offset" with a CGSize, you can see the top of the shadow is shifted right about 35 degrees and slightly squeezed the height.
When we tap and hold and pin, the shadow is also animated away from the pin, so I believe that such shadow can be made programmably.
The best shadow tutorial I can found so far is http://nachbaur.com/blog/fun-shadow-effects-using-custom-calayer-shadowpaths But unfortunately, that cannot make this effect.
If anyone know the answer or know any better words to search for, please let me know. Thank you.
(Please note that the shape of the image is dynamic in the App, so using any tool like Photoshop to pre-render the shadow is not an option.)
In order to create dynamic effects like this, you have to use Core Graphics. It's incredibly powerful once you know how to use it. Basically you need to set a skew transform on the context, set up a shadow and draw the image. You will probably have to use transparency layers as well.
It doesn't sound like you can use CALayer shadows, since that is meant to solve a specific use-case. The approach Apple takes with the pin marks on the map is to have two separate images that are created ahead of time (e.g. in Photoshop) and they position them within the map relative to a reference point.
If you really do need to do this at run-time, it should still be possible by using either Core Graphics or ImageKit. To get a blurred shadow appearance, you can use the kCICategoryBlur CIFilter. You can then convert the image to grayscale. And to get that compressed look you just need to resize and skew the image.
Once you have two separate images, you can either take the CGImageRef for the shadow image and can set that as the content of another sublayer, or you can add it as a separate view.
If you know what all the shapes are, you could just render a shadow image in Photoshop or something.

iOS Quartz/CoreGraphics drawing feathered stroke

I am drawing a path into a CGContext following a set of points collected from the user. There seems to be some random input jitter causing some of the line edges to look jagged. I think a slight feather would solve this problem. If I were using OpenGL ES I would simply apply a feather to the sprite I am stroking the path with; however, this project requires me to stay in Quartz/CoreGraphics and I can't seem to find a similar solution.
I have tried drawing 5 lines with each line slightly larger and more transparent to approximate a feather. This produces a bad result and slows performance noticeably.
This is the line drawing code:
CGContextMoveToPoint(UIGraphicsGetCurrentContext(),((int)lastPostionDrawing1.x), (((int)lastPostionDrawing1.y)));
CGContextAddCurveToPoint(UIGraphicsGetCurrentContext(), ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, lastPostionDrawing2.x, lastPostionDrawing2.y;
[currentPath addCurveToPoint:CGPointMake(lastPostionDrawing2.x-((int)furthestLeft.x)+((int)penSize), lastPostionDrawing2.y controlPoint1:CGPointMake(ctrl1_x, ctrl1_y) controlPoint2:CGPointMake(ctrl2_x, ctrl2_y)];
I'm going to go ahead and assume that your CGContext still has anti-aliasing turned on, but if not, then that's the obvious first think to try, as #Davyd's comment suggests: CGContextSetShouldAntialias is the function of interest.
Assuming that's not the problem, and the line is being anti-aliased by the context, but you're still wanting something 'softer.' I can think of a couple of ways to do this that should hopefully be faster than stroking 5 times.
First, you can try getting the stroked path (i.e. a path that describes the outline of the stroke of the current path) using CGContextReplacePathWithStrokedPath you can then fill this path with a gradient (or whatever other fill technique gives the desired results.) This will work well for straight lines, but won't be straightforward for curved paths (since the gradient is filling the area of the stroked path, and will be either linear or radial.)
Another perhaps less obvious option, might be to abuse CG's shadow drawing for this purpose. The function you want to look up is: CGContextSetShadowWithColor Here's the method:
Save the GState: CGContextSaveGState
Get the bounding box of the original path
Copy the path, translating it away from itself by 2.0 * bbox.width using CGPathCreateCopyByTransformingPath (note: use the X direction only, that way you don't need to worry about flips in the context)
Clip the context to the original bbox using CGContextClipToRect
Set a shadow on the context with CGContextSetShadowWithColor:
Some minimal blur (Start with 0.5 and go from there. The blur parameter is non-linear, and IME it's sort of a guess and check operation)
An offset equal to -2.0 * bbox width, and 0.0 height, scaled to base space. (Note: these offsets are in base space. This will be maddening to figure out, but assuming you're not adding your own scale transforms, the scale factor will either be 1.0 or 2.0, so practically speaking, you'll be setting an offset.width of either -2.0*bbox.width or -4.0*bbox.width)
A color of your choosing.
Stroke the translated-away path.
Pop the GState CGContextRestoreGState
This should leave you with "just" the shadow, which you can hopefully tweak to achieve the results you want.
All that said, CG's shadow drawing performance is, IME, less than completely awesome, and less than completely deterministic. I would expect it to be faster than stroking the path 5 times with 5 different strokes, but not overwhelmingly so.
It'll come down to how much achieving this effect is worth to you.