well I know that every imageViews have a layer (we can access it with :imageView.layer).I would like to use the layer of my imageView and add a subLayer to it's layer:shapeLayer(that is a CAShapeLayer) My code doesn't work, it doesn't show the shape layer!
- (void)viewDidLoad
{
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
[self.view addSubview:imageView];
}
- (void)anotherMethod
{
[imageView.layer addSublayer:shapeLayer];
}
How can I solve this please ?
Try this:
[outputImage.layer insertSublayer:shapeLayer atIndex:0];
I solved similar problem by using UIView below UIImageView and setting a layer to UIView. When you add a layer to UIImageView at index 0, your image will not be visible.
I have a UIView called photoView. It has a subview of class UIImageView, that has same bounds and has a clear background color. And I add a gradientLayer to photoView.
- (void)viewDidLoad
{
[super viewDidLoad];
// Adding gradient background to UIImageView
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.photoImageView.bounds;
gradientLayer.colors = #[(id)[UIColor customUltraLightGrayColor].CGColor, (id)[UIColor customUltraLightGrayColor].CGColor, (id)[UIColor customDarkGrayColor].CGColor];
gradientLayer.locations = #[#(0.0), #(0.6)];
[self.photoView.layer insertSublayer:gradientLayer atIndex:0];
}
As a result I get this:
This could have several reasons, for example:
Did you #import <QuartzCore/QuartzCore.h> (and / or CoreGraphics/CoreGraphics.h> depending on what you are doing in the rest of your code)?
You could have set a wrong CGRectMake when initializing the CALayer which is not inside your visible frame (Like CGRectMake(5000,5000,...,...)) or one of the size properties is 0?
Try setting the frame for layer:
Follow the three steps
CALayer layerToBeAdded = [[CALayer alloc] init];
[layerToBeAdded setFrame:imgView.frame];
[imgView.layer addSublayer:layerToBeAdded];
Instead of allocating CALayer, you can use [imgView layer]
And to add image to layer do it like this
layerToBeAdded.contents = (id)imgXYZ.CGImage;
Assuming that you are talking about just one UIImageView (imageView == outputImage ???)
//in another method:
{
[imageView.layer addSublayer:shapeLayer]; //Where is outputImage initialized?
}
Here is a solution:
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];
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
I'm trying to slide down an image in an UIImageView, but I don't know which combination of UIContentMode and animation property is the right one to make that happen.
The image should always have the same size and should not be streched... all I want is, that nothing is visible first and then the frame extends and reveals the image.
Maybe it's easier if you see what I mean:
So, it sounds rather easy, but what UIContentMode should I use for the UIImageView and what property should I animate? Thank you!
I took your lead and made a screencast as well. Was this what you had in mind?
I put the animation repeating indefinitely so it would be easier to capture with a video, but it can be started at the press of a button, as well as frozen in place, showing the popover and its contents, until reversed to be hidden again.
I used Core Animation for that, instead of animating a UIView, since I wanted to use the mask property of CALayer to hide the popover and reveal it with a sliding animation.
Here is the code I used (same as in the video):
- (void)viewDidLoad
{
// Declaring the popover layer
CALayer *popover = [CALayer layer];
CGFloat popoverHeight = 64.0f;
CGFloat popoverWidth = 200.0f;
popover.frame = CGRectMake(50.0f, 100.0f, popoverWidth, popoverHeight);
popover.contents = (id) [UIImage imageNamed:#"popover.png"].CGImage;
// Declaring the mask layer
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0, - popoverHeight, popoverWidth, popoverHeight);
maskLayer.backgroundColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f].CGColor;
// Setting the animation (animates the mask layer)
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position.y"];
animation.byValue = [NSNumber numberWithFloat:popoverHeight];
animation.repeatCount = HUGE_VALF;
animation.duration = 2.0f;
[maskLayer addAnimation:animation forKey:#"position.y"];
//Assigning the animated maskLayer to the mask property of your popover
popover.mask = maskLayer;
[self.view.layer addSublayer:popover];
[super viewDidLoad];
}
NOTE: You have to import the QuartzCore framework into your project and write this line in your header file: #import <QuartzCore/QuartzCore.h>.
Tells if this works for you or if you need any more help setting this up.
Try this code.
Consider the UIImageView as imageView.
imageView.contentMode = UIViewContentModeScaleAspectFill;
CGRect imageRect = imageView.frame;
CGRect origImgRect = imageRect;
imageRect.size.height = 5;
imageView.frame = imageRect;
[UIView animateWithDuration:2.0
animations:^{imageView.rect = origImgRect;}
completion:^(BOOL finished){ }];
I want to add shadow effect for UINavigationbar like GameCenter.
I think apply background image with shadow to nav bar, but title's line height would be down.
And I draw shadow to background, but background image would not scroll.
What is the best Practice of this case??
You can subclass UINavigationController and then have a shadow layer for each navigation or if your bar is always visible just add the shadow to UIWindow (only one for the entire application) and then make it the frontmost view each time you add a subview.
CGColorRef darkColor = [[UIColor blackColor] colorWithAlphaComponent:.5f].CGColor;
CGColorRef lightColor = [UIColor clearColor].CGColor;
CAGradientLayer *newShadow = [[[CAGradientLayer alloc] init] autorelease];
newShadow.frame = CGRectMake(0, self.navigationBar.frame.size.height, self.navigationBar.frame.size.width, 10);
newShadow.colors = [NSArray arrayWithObjects:(id)darkColor, (id)lightColor, nil];
[self.navigationBar.layer addSublayer:newShadow];
If you choose the latter case then override the didAddSubview to make the layer the frontmost:
CALayer *superlayer = self.shadowLayer.superlayer;
[self.shadowLayer removeFromSuperlayer];
[superlayer addSublayer:self.shadowLayer];
Hope it helps.
It's easly done with custom subclass of UINavigationController. The key is to overwrite -viewWillAppear: and add sublayer to UINavigationController's view's layer:
- (void)viewWillAppear:(BOOL)animated
{
CGColorRef darkColor = [[UIColor blackColor] colorWithAlphaComponent:.5f].CGColor;
CGColorRef lightColor = [UIColor clearColor].CGColor;
CGFloat navigationBarBottom;
navigationBarBottom = self.navigationBar.frame.origin.y + self.navigationBar.frame.size.height;
CAGradientLayer *newShadow = [[[CAGradientLayer alloc] init] autorelease];
newShadow.frame = CGRectMake(0,navigationBarBottom, self.view.frame.size.width, 10);
newShadow.colors = [NSArray arrayWithObjects:(id)darkColor, (id)lightColor, nil];
[self.view.layer addSublayer:newShadow];
[super viewWillAppear:animated];
}
navigationBarBottom accounts for both UINavigationBar and status bar.
Credit for gradient layer parameters goes to marcio.
Drawing a CAGradientLayer gives a very uniform, but - in my opinion - a rather unnatural looking shadow.
Here's an alternative approach based upon the answer to question UIView with shadow.
It uses the various shadow properties of CALayer to give the shadow effect:
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationController.navigationBar.layer.shadowColor = [[UIColor blackColor] CGColor];
self.navigationController.navigationBar.layer.shadowOffset = CGSizeMake(0.0f,0.0f);
self.navigationController.navigationBar.layer.shadowOpacity = 1.0f;
self.navigationController.navigationBar.layer.shadowRadius = 4.0f;
}
The same technique works with the tabBarController's tabBar;
self.tabBarController.tabBar.layer.shadowColor = [[UIColor blackColor] CGColor];
self.tabBarController.tabBar.layer.shadowOffset = CGSizeMake(0.0f,0.0f);
self.tabBarController.tabBar.layer.shadowOpacity = 1.0f;
self.tabBarController.tabBar.layer.shadowRadius = 4.0f;
2 things i want to do, which are related:
Show a block of any colour. So i could change that colour to something else at any time.
Tint a UIImage to be a different colour. An overlay of colour with alpha turned down could work here, but say it was an image which had a transparent background and didn't take up the full square of the image.
Any ideas?
Another option, would be to use category methods on UIImage like this...
// Tint the image, default to half transparency if given an opaque colour.
- (UIImage *)imageWithTint:(UIColor *)tintColor {
CGFloat white, alpha;
[tintColor getWhite:&white alpha:&alpha];
return [self imageWithTint:tintColor alpha:(alpha == 1.0 ? 0.5f : alpha)];
}
// Tint the image
- (UIImage *)imageWithTint:(UIColor *)tintColor alpha:(CGFloat)alpha {
// Begin drawing
CGRect aRect = CGRectMake(0.f, 0.f, self.size.width, self.size.height);
UIGraphicsBeginImageContext(aRect.size);
// Get the graphic context
CGContextRef c = UIGraphicsGetCurrentContext();
// Converting a UIImage to a CGImage flips the image,
// so apply a upside-down translation
CGContextTranslateCTM(c, 0, self.size.height);
CGContextScaleCTM(c, 1.0, -1.0);
// Draw the image
[self drawInRect:aRect];
// Set the fill color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextSetFillColorSpace(c, colorSpace);
// Set the mask to only tint non-transparent pixels
CGContextClipToMask(c, aRect, self.CGImage);
// Set the fill color
CGContextSetFillColorWithColor(c, [tintColor colorWithAlphaComponent:alpha].CGColor);
UIRectFillUsingBlendMode(aRect, kCGBlendModeColor);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Release memory
CGColorSpaceRelease(colorSpace);
return img;
}
The first one is easy. Make a new UIView and set its background color to whatever color you’d like.
The second is more difficult. As you mentioned, you can put a new view on top of it with transparency turned down, but to get it to clip in the same places, you’d want to use a mask. Something like this:
UIImage *myImage = [UIImage imageNamed:#"foo.png"];
UIImageView *originalImageView = [[UIImageView alloc] initWithImage:myImage];
[originalImageView setFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
[parentView addSubview:originalImageView];
UIView *overlay = [[UIView alloc] initWithFrame:[originalImageView frame]];
UIImageView *maskImageView = [[UIImageView alloc] initWithImage:myImage];
[maskImageView setFrame:[overlay bounds]];
[[overlay layer] setMask:[maskImageView layer]];
[overlay setBackgroundColor:[UIColor redColor]];
[parentView addSubview:overlay];
Keep in mind you’ll have to #import <QuartzCore/QuartzCore.h> in the implementation file.
Here is another way to implement image tinting, especially if you are already using QuartzCore for something else.
Import QuartzCore:
#import <QuartzCore/QuartzCore.h>
Create transparent CALayer and add it as a sublayer for the image you want to tint:
CALayer *sublayer = [CALayer layer];
[sublayer setBackgroundColor:[UIColor whiteColor].CGColor];
[sublayer setOpacity:0.3];
[sublayer setFrame:toBeTintedImage.frame];
[toBeTintedImage.layer addSublayer:sublayer];
Add QuartzCore to your projects Framework list (if it isn't already there), otherwise you'll get compiler errors like this:
Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"
An easy way to achieve 1 is to create a UILabel or even a UIView and change the backgroundColor as you like.
There is a way to multiply colours instead of just overlaying them, and that should work for 2. See this tutorial.
Try this
- (void)viewDidLoad
{
[super viewDidLoad];
UIView* maskedView = [self filledViewForPNG:[UIImage imageNamed:#"mask_effect.png"]
mask:[UIImage imageNamed:#"mask_image.png"]
maskColor:[UIColor colorWithRed:.6 green:.2 blue:.7 alpha:1]];
[self.view addSubview:maskedView];
}
-(UIView*)filledViewForPNG:(UIImage*)image mask:(UIImage*)maskImage maskColor:(UIColor*)maskColor
{
UIImageView *pngImageView = [[UIImageView alloc] initWithImage:image];
UIImageView *maskImageView = [[UIImageView alloc] initWithImage:maskImage];
CGRect bounds;
if (image) {
bounds = pngImageView.bounds;
}
else
{
bounds = maskImageView.bounds;
}
UIView* parentView = [[UIView alloc]initWithFrame:bounds];
[parentView setAutoresizesSubviews:YES];
[parentView setClipsToBounds:YES];
UIView *overlay = [[UIView alloc] initWithFrame:bounds];
[[overlay layer] setMask:[maskImageView layer]];
[overlay setBackgroundColor:maskColor];
[parentView addSubview:overlay];
[parentView addSubview:pngImageView];
return parentView;
}
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.