I am trying to distort an image in an arbitrary way, following for example a bezier shape. I have used CAShapeLayer to create the shape, but then it seems that setting its contents to an Image does not work. How can I have the image distorted following that shape? Is it even possible?
Thank you!
it is not possible to do so, but i succeeded in doing so by putting a CALayer on top a CAShapeLayer.
this is my code:
CALayer *image =[CALayer layer];
image.frame = CGRectMake(0, 0, 75, 75);
image.contents = (id) [UIImage imageNamed:#"number-1"].CGImage;
[self.layer addSublayer:image];
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 75, 75)];
blueCircleLayer = [CAShapeLayer layer];
blueCircleLayer.path = circle.CGPath;
blueCircleLayer.strokeColor = [UIColor blueColor].CGColor;
blueCircleLayer.fillColor = [UIColor clearColor].CGColor;
blueCircleLayer.shadowColor =[UIColor blackColor].CGColor;
blueCircleLayer.shadowOffset = CGSizeMake(0.0f, 5.0f);
blueCircleLayer.shadowOpacity = 0.7f;
blueCircleLayer.lineWidth = 7.0;
[self.layer addSublayer:blueCircleLayer];
This is currently not possible. I would suggest filing a radar (http://bugreport.apple.com) if you think this should be supported.
See also: Can a CGImage be added to the contents property of a CAShapeLayer?
Related
I seem to be having difficulties adding a mask via CALayers. I'm simply trying to mask a UIImageView. Here's my code:
CALayer *maskLayer = [CALayer layer];
UIImage *mask = [UIImage imageNamed:#"mask.png"];
maskLayer.contents = mask;
UIImageView *viewToMask = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
viewToMask.image = [UIImage imageNamed:#"picture.png"];
viewToMask.layer.mask = maskLayer;
[self.view addSubview:viewToMask];
Mask.png is black with a transparent circle punched through it (is this correct way to mask?). I'm not sure where this is failing, perhaps at maskLayer.contents since its supposed to be a CGImageRef but I get errors when I set it as mask.CGImage, or through a local variable CGImageRef = mask.CGImage. Anyway, the way its set now doesn't give errors, so I hope its fine.
Does anyone know what's going on, or how to properly set masks with CALayers? Thanks
Try
maskLayer.contents = (id)mask.CGImage;
Yes, the cast sucks, but it's necessary.
I think you'll also need to say
maskLayer.bounds = (CGRect){CGPointZero, mask.size};
try this:
CALayer *maskLayer = [CALayer layer];
UIImage *mask = [UIImage imageNamed:#"mask.png"];
maskLayer.contents = (id)mask.CGImage;
// maskLayer.contentsGravity = kCAGravityCenter;
maskLayer.frame = CGRectMake(0.0, 0.0,1024,768);
UIImageView *viewToMask = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
viewToMask.image = [UIImage imageNamed:#"picture.png"];
viewToMask.layer.mask = maskLayer;
[self.view addSubview:viewToMask];
you also need to set mask frame
something exactly like above?
I know how to produce a rounded corner:
imageView.layer.cornerRadius = 10;
imageView.layer.shouldRasterize = YES;
imageView.layer.masksToBounds = YES;
For the shadow, I have tried
imageView.layer.shadowOffset = CGSizeMake(1, 1);
imageView.layer.shadowRadius = 5;
imageView.layer.shadowOpacity = 0.4;
imageView.layer.shadowColor = [UIColor blackColor].CGColor;
imageView.layer.shouldRasterize = YES;
But imageView.layer.masksToBounds = YES; from rounded corner kills the shadow.
Another question is that how to produce a shadow exactly like shown in the image? I produced this image in photoshop, I used 120 degree as the direction of the light. But if I used the code above, and turn off maskToBounds, I can see the shadow and it is ugly.
Or can I produce a rounded corner+shadow image frame in photoshop and apply the frame to every image in my app? I think that will give better performance. shadowing and cornering the images on the fly will have terrible performance if all images are on a scroll.
Thanks
Try this :
CALayer *sublayer = [CALayer layer];
sublayer.backgroundColor = [UIColor blueColor].CGColor;
sublayer.shadowOffset = CGSizeMake(0, 3);
sublayer.shadowRadius = 5.0;
sublayer.shadowColor = [UIColor blackColor].CGColor;
sublayer.shadowOpacity = 0.8;
sublayer.frame = CGRectMake(30, 30, 128, 192);
sublayer.borderColor = [UIColor blackColor].CGColor;
sublayer.borderWidth = 2.0;
sublayer.cornerRadius = 10.0;
[self.view.layer addSublayer:sublayer];
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = sublayer.bounds;
imageLayer.cornerRadius = 10.0;
imageLayer.contents = (id) [UIImage imageNamed:#"BattleMapSplashScreen.jpg"].CGImage;
imageLayer.masksToBounds = YES;
[sublayer addSublayer:imageLayer];
And take look at the original source
I would use two image views. A background png that has the shadow (can be re-used for every image) and the foreground png image which has rounded corners.
You probably want to use a different layer for shadowing, and keep your masksToBounds and rounding code. In this example imageView is the name the image view that you want to shadow and round:
CALayer *shadowLayer = [[[CALayer alloc] init] autorelease];
sublayer.frame = imageView.bounds;
sublayer.masksToBounds = NO;
sublayer.shadowOffset = CGSizeMake(1, 1);
sublayer.shadowRadius = 5;
sublayer.shadowOpacity = 0.4;
sublayer.shadowColor = [UIColor blackColor].CGColor;
sublayer.shouldRasterize = YES;
[imageView.layer insertSublayer:shadowLayer atIndex:0]; // Put it underneath the image
That should give you the shadow. Now to avoid the slow recalculating, I suggest creating a UIImage out of the image. See this link: Create UIImage from shadowed view while retaining alpha?.
Hope this helps!
I'm creating a CALayer like this:
UIColor *color = [UIColor colorWithPatternImage:[UIImage imageNamed:#"MyPattern.png"]];
backgroundLayer = [[CALayer alloc] init];
[backgroundLayer setBackgroundColor:[color CGColor]];
[[self layer] addSublayer:backgroundLayer];
For some reason, the pattern is drawn upside down. I've already tried setting geometryFlipped and applying sublayer transforms, but it doesn't work.
Using [backgroundLayer setTransform:CATransform3DMakeScale(1.0, -1.0, 1.0)]; actually does work.
I would rather use this method:
CALayer *backgroundLayer = [CALayer layer];
backgroundLayer.frame = CGRectMake(50, 50, 200, 200);
UIImage *img = [UIImage imageNamed:#"MyPattern.png"];
CGImageRef imgRef = CGImageRetain(img.CGImage);
backgroundLayer.contents = (id) imgRef;
[self.layer addSublayer:backgroundLayer];
This will provide image content inside the layer and it fits automatically, so it works great for backgrounds. Im not sure why your method isnt working, sorry about that, but I thought it would be nice to provide you with this method.
Instead of using CATransform3DMakeScale you can flip image yourself:
UIImage* img = [UIImage imageNamed:#"MyPattern.png"];
img = [UIImage imageWithCGImage:img.CGImage
scale:img.scale
orientation:UIImageOrientationDownMirrored];
UIColor* color = [UIColor colorWithPatternImage:img];
…
This works fine for me.
Or you can go further and actually redraw the image.
- (UIImage*)flipImage:(UIImage*)image {
UIGraphicsBeginImageContext(image.size);
CGContextDrawImage(UIGraphicsGetCurrentContext(),CGRectMake(0.,0., image.size.width, image.size.height),image.CGImage);
UIImage* img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
Details why it can be useful are in this answer.
I am having a lot of trouble trying to find out how to draw a transparent circle on top of a UIImage within my UIImageView. Google-ing gives me clues, but I still can't find a working example.
Are there any examples that anyone knows of that demonstrate this?
Easiest way is simply to create a semi-transparent square UIView, then set the cornerRadius of its layer to be half of its width/height. Something like:
UIView *squareView = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
squareView.alpha = 0.5;
squareView.layer.cornerRadius = 50;
...
[squareView release];
This has got to be the simplest solution:
CGFloat r = 150;
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0,0,1.5*r,1.5*r)];
lbl.text = #"●";
lbl.transform = CGAffineTransformMakeTranslation(0.0f, -r/6);
lbl.textAlignment = UITextAlignmentCenter;
lbl.backgroundColor = [UIColor clearColor];
lbl.textColor = [UIColor redColor];
lbl.font = [UIFont systemFontOfSize:2*r];
lbl.alpha = 0.5;
lbl.center = self.view.center;
[self.view addSubview:lbl];
One way would be to add a CAShapeLayer with a circular path, either directly to the layer of the UIImageView or as the layer of a new UIView that is added to the UIImageView.
If you actually want to modify the image, then create a mutable copy of it by drawing it into a CGBitmapContext then creating a new image from the modified bitmap.
CGPathRef circlePath = CGPathCreateMutable();
CGPathAddEllipseInRect( circlePath , NULL , CGRectMake( 0,0,20,20 ) );
CAShapeLayer *circle = [[CAShapeLayer alloc] init];
circle.path = circlePath;
circle.opacity = 0.5;
[myImageView.layer addSublayer:circle];
CGPathRelease( circlePath );
[circle release];
You can implement a custom sub-class of UIView that draws your image and then the circle in the drawRect method:
#interface CircleImageView : UIView {
UIImage * m_image;
CGRect m_viewRect;
// anything else you need in this view?
}
Implementation of drawRect:
- (void)drawRect:(CGRect)rect {
// first draw the image
[m_image drawInRect:m_viewRect blendMode:kCGBlendModeNormal alpha:1.0];
// then use quartz to draw the circle
CGContextRef context = UIGraphicsGetCurrentContext ()
// stroke and fill black with a 0.5 alpha
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 0.5);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.5);
// now draw the circle
CGContextFillEllipseInRect (context, m_viewRect);
}
You will need to set up the m_viewRect and m_image member functions on init.
According to the Mac Dev Center docs, you should be able to set the contents property of a CALayer and have that render automatically. However, I still can't get a simple image to show up by adding a sublayer to the UIView's root later. I've tried multiple different variations; here's what I have so far:
(Note: I know there are other ways of rendering images; for my purposes I'd like to use CALayer's for some of the more complicated stuff I'm going to get into).
(in viewDidDisplay() of the ViewController):
CALayer *theLayer = [CALayer layer];
[[[self view] layer] addSublayer:theLayer];
theLayer.contents = (id)[[UIImage imageNamed:#"mypic.png"] CGImage];
theLayer.contentsRect = CGRectMake(0.0f, 0.0f, 300.0f, 300.0f);
theLayer.bounds = CGRectMake(0.0f, 0.0f, 300.0f, 400.0f);
Anyone know what I'm doing wrong?
Thanks!
You could load the image into a UIImageView, which is decended from UIView and therefore has it's own layer property.
UIImageView *imgView = [[UIImageView alloc] initWithFrame:frame];
imgView.image = [UIImage imageNamed:#"mypic.png"];
[[[self view] layer] addSublayer:[imgView layer]];
[imgView release];
You don't need to set the contentsRect (and if you do, it should be in the unit coordinate space, probably just CGRectMake(0, 0, 1.0, 1.0).
You might want to set the layer's position property.
You need to create two CALayer . This is perfect way to display the image within the CALayer.
CALayer *pulseLayer_ = [[CALayer layer] retain];
pulseLayer_.backgroundColor = [[UIColor whiteColor] CGColor];
pulseLayer_.bounds = CGRectMake(0., 0., 80., 80.);
pulseLayer_.cornerRadius = 12.;
pulseLayer_.position = self.view.center;
[self.view.layer addSublayer:pulseLayer_];
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = pulseLayer_.bounds;
imageLayer.cornerRadius = 10.0;
imageLayer.contents = (id) [UIImage imageNamed:#"jacklogo.png"].CGImage;
imageLayer.masksToBounds = YES;
[pulseLayer_ addSublayer:imageLayer];
[pulseLayer_ setNeedsDisplay];
I think its make solution to your problem.