My app runs on Mac and iOS. (two code bases with lots of shared code).
I have a piece of common code that converts a shape to a NSBezierPath/UIBezierPath.
The code is identical between the two - except for the NS*/UI*.
I then create a CAShapeLayer and set the path for the layer to the Bezier path.
That layer is added to a set of layers in the view. Again... common code between iOS and Mac.
One type of shape is a simple circle.
To create this I call:
path.AddArc(point.CGPoint(), width/2, 0, (float)(2 * Math.PI), true);
When I add this to my layer... I get a clipped circle, but only on the Mac. On iOS this works perfectly.
I tried switching to AppendPathWithOvalInRect:
path.AppendPathWithOvalInRect(rect);
That provides a slightly different clip direction:
The layer construction is very simple, and identical between Mac and iOS.
var layer = new CAShapeLayer();
layer.LineWidth = 0f;
layer.FillColor = color;
layer.StrokeColor = color;
layer.Path = GetBezier().CGPath();
objectLayer.AddSubLayer(layer);
I can see no evidence of masks or clipping on any other object. Just all my circles, and only on the Mac.
I've also included below an example of a number of simple objects - lines, fat lines, circles. You can see that all circles have this weird clipping.
Any thoughts on what I might be missing here?
Thx for your help...
I have a solution.
It turns out I was missing a ClosePath().
Interestingly... iOS does not need the ClosePath call.
On the Mac, the following calls do need a ClosePath call:
AddArc, FromOvalInRect, AppendPathWithArc.
The following call did not seem to need a ClosePath call:
FromRoundedRect
because I could use that to draw a perfect circle.
Related
I used the scanning line seed filling algorithm to realize the color filling function.
But I don't know how to do that. When my pen lands in the flower, I need to draw only Inside the black edge of the flower,do not draw in the hair.
I have tried to use :
CALayer *layer = [CALayer layer];
layer.contents = (__bridge id)(image.CGImage);
self.drawView.layer.mask = layer;
But the plan didn't work.
if any one gives solution it would be so great,Thank you.
(English is not my native language; please excuse typing errors.)
Use a two pass approach.
Use the same algorithm as the fill algorithm to create a stencil mask which fills the area you want to allow the pen to draw.
Draw the pen with stencil testing to constrain it to the allowed region.
Similar approach could be used to create an alpha-mask on the fly (e.g. as a texture), and mix that with the pen drawing.
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've got a CAShapeLayer and was trying to draw it by passing it to CGContextDrawLayerAtPoint but get an error about passing a retainable parameter of CAShapeLayer *__strong to a function expecting a CGLayerRef.
I've done some browsing but can't figure out how to convert it/bridge the CAShapeLayer to the CGLayerRef.
If its not possible to convert, then can a CAShapeLayer be created using CGLayerCreateWithContext?
What I'm doing is creating a drawing with a UIBezierPath, then creating a CAShapeLayer and setting its path to the UIBezierPath then I want to display it. I could use addSublayer to display the shape layer however I'm going to display the same shape at multiple different points so rather than add multiple sublayers I was planning on using CGContextDrawLayerAtPoint to display it at different points.
Short answer: no.
Long answer: CAShapeLayer is a Core Animation layer (CALayer). You put it into your interface and it draws itself.
I know it's confusing, the way they use the word "layer", but CGContextDrawLayerAtPoint has nothing to do with that (it's using the word "layer" in a totally different way, referring to a CGLayer, which is completely different and is used utterly differently). You've gone down the wrong rabbit hole here.
I could use addSublayer: to display the shape layer
Not could. Must. That is what you do with a CAShapeLayer.
The alternative you are looking for would be to pull the CGPath out of the CAShapeLayer and assign that path and stroke it, multiple times in multiple places, in a CGContext. But there is really no need to do that. iOS drawing is all about layers; don't be afraid of having multiple sublayers.
I'm trying to make an animation very similar to the one at the start of Air bnb iOS app.
Here's a video of the animation : video
The idea is to simulate a layer flying from being very close to the user to end sticking on a far away surface.
I've read some articles talking about manipulating the layer.transform.m34 and the one that helped me more is this one.
By applying perspective and a translation on the z-axis, I managed to get the layer look bigger.
Here's the code I used :
CALayer *aLayer = [CALayer layer];
aLayer.frame = ...
aLayer.backgroundColor = ...
CATransform3D perspectiveTransform = CATransform3DIdentity;
perspectiveTransform.m34 = 1.0f/-250.0f;
perspectiveTransform.m44 = 0.0f;
perspectiveTransform = CATransform3DTranslate(perspectiveTransform, 0.0f, 0.0f. -100.0f);
aLayer.transform = perspectiveTransform;
The problem is I can't get it to animate back to CATransform3DIdentity .
I'm not used to CoreAnimation so I may be trying a bad approach.
It would help a lot if someone could point me to what I'm doing wrong or to a better solution.
Thanks in advance!
You need to first create your layer and add it to the layer tree. Once the layer is part of the layer tree then implicit animations should work.
I think you may need to do this:
Create layer
Add layer to parent layer
run remaining animation code with performSelector:withObject:afterDelay: so system gets a chance to add the layer to the layer before your code to do implicit animations is run.
I've got some card games which use CALayers to draw individual cards. There can easily be 40 or 50 of them on the screen, which usually works fine.
I recently tried to turn on their shadows using the simple properties for CALayers:
theCardLayer.shadowOffset = CGSizeMake(3,2);
theCardLayer.shadowOpacity = 0.7f;
At that point, the program started getting really laggy. Fair enough; some of the docs said that the shadows could be CPU-intensive.
Any ideas for how to efficiently draw shadows on everything? They're all on the same CALayer in the same UIView, so I'm wondering if there might be a way to pull the mask of the layer or its UIView and shadow that, or something ...
Any functionality up to iOS5 is fair game.
At the very least, try setting your layer's shadowPath property. It can make shadow rendering significantly faster.
Kurt offered up the correct solution. Here's an example of how to use a shadowPath:
UIBezierPath *thisCLPath = [UIBezierPath
bezierPathWithRoundedRect:theCardLayer.bounds
cornerRadius:10.0f];
theCardLayer.shadowPath = thisCLPath.CGPath;
Clearly, I'm using rounded corners here. For a straight-edged layer, you can just use bezierPathWithRect:. There are a few other helpful methods in UIBezierPath as well.
The result is just the right side of laggy on older iOS devices (like an iPhone4 or a mid-generation iPod Touch) and blazing on an iPad3.