I need to make UIImageView transparent at the Middle Position(Round Circle ..See attached image)
I should be able to see the Behind Subviews of UIImageView via that middle circle...i tried CAShapeLayer..but i am not able to see the behind subviews of UIImageView..Please help me..Sorry for my English....
CAShapeLayer *circleLayer = [CAShapeLayer layer];
// Give the layer the same bounds as your image view
[circleLayer setBounds:CGRectMake(100.0f, 100.0f, [((UIImageView *)view) bounds].size.width,
[((UIImageView *)view) bounds].size.height)];
// Position the circle anywhere you like, but this will center it
// In the parent layer, which will be your image view's root layer
[circleLayer setPosition:CGPointMake(100,
100)];
// Create a circle path.
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:
CGRectMake(100.0f, 100.0f, 50.0f, 50.0f)];
// Set the path on the layer
[circleLayer setPath:[path CGPath]];
// Set the stroke color
[circleLayer setStrokeColor:[[UIColor redColor] CGColor]];
// Set the stroke line width
[circleLayer setLineWidth:2.0f];
// Add the sublayer to the image view's layer tree
[circleLayer setOpacity:0.5];
[[((UIImageView *)view) layer] addSublayer:circleLayer];
One way to do it is like this. I have an image view with my main image, and another image called BlackCircle which is just a 40x40 black circle (that I made in the Pixen app). The action method causes the main image to become transparent where it is overlapped by the black circle, which I've placed in the center of the main image:
#interface ViewController ()
#property (weak,nonatomic) IBOutlet UIImageView *imageView;
#end
#implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
self.imageView.image = [UIImage imageNamed:#"House.tiff"];
self.imageView.backgroundColor = [UIColor clearColor];
}
-(IBAction)createAlphaClippedImage:(id)sender {
UIImage *circleImage = [UIImage imageNamed:#"BlackCircle.png"];
UIImage *mainImage = [UIImage imageNamed:#"House.tiff"];
UIGraphicsBeginImageContextWithOptions(mainImage.size, NO, 0.0);
CGRect imageRect = CGRectMake(0.0f, 0.0f, mainImage.size.width, mainImage.size.height);
CGRect centerCircleRect = CGRectMake(mainImage.size.width/2.0 - circleImage.size.width/2.0, mainImage.size.height/2.0 - circleImage.size.height/2.0,circleImage.size.width,circleImage.size.height);
[mainImage drawInRect:imageRect];
[circleImage drawInRect:centerCircleRect blendMode:kCGBlendModeXOR alpha:1.0];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = image;
}
Instead of trying to make a portion of Imageview transparent, try to make in image transparent at areas you want it to by setting corresponding alphas to 0.
Related
I currently have the following image on which I am trying to set a border. It consists of an UIImageView with an image inside (a transparent.png)
When I try to set the border for my image (see code), it gives a border to the UIImage, but it doesn't 'snap' around my image. Is it possible to achieve that effect?
See image current implementation here.
- (UIImage*)imageWithBorderFromImage:(UIImage*)source;
{
CGSize size = [source size];
UIGraphicsBeginImageContext(size);
CGRect rect = CGRectMake(0, 0, size.width, size.height);
[source drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.5, 1.0, 1.0);
CGContextStrokeRect(context, rect);
UIImage *testImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return testImg;
}
Try Adding a layer behind UIImageView and add a border to it that will do the trick
#define kBorderWidth 3.0.
#define kCornerRadius 8.0
CALayer *borderLayer = [CALayer layer];
CGRect borderFrame = CGRectMake(0, 0, (imageView.frame.size.width), (imageView.frame.size.height));
[borderLayer setBackgroundColor:[[UIColor clearColor] CGColor]];
[borderLayer setFrame:borderFrame];
[borderLayer setCornerRadius:kCornerRadius];
[borderLayer setBorderWidth:kBorderWidth];
[borderLayer setBorderColor:[[UIColor redColor] CGColor]];
[imageView.layer addSublayer:borderLayer];
And don't forget to import QuartzCore/QuartzCore.h
This example will draw a boarder on the layer, but change it's frame slightly to make the border around the layer.
Depending on your needs, if you don't want it to be as accurate as possible, a quick and dirty solution could be something like this:
- (UIImage *)borderedImageFromImage:(UIImage *)source andColor:(UIColor *)borderColor{
CGFloat scale = 0.95;//this determines how big the border will be, the smaller it is the bigger the border
UIImage *borderImage = [source imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIGraphicsBeginImageContextWithOptions(source.size, NO, source.scale);
[borderColor set];
[borderImage drawInRect:CGRectMake(0, 0, source.size.width, source.size.height)];
[source drawInRect:CGRectMake(source.size.width*(1-scale)/2,
source.size.height*(1-scale)/2,
source.size.width * scale,
source.size.height * scale)];
borderImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return borderImage;
}
and here is how to use it:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.heartImageView.image = [self borderedImageFromImage:[UIImage imageNamed:#"heart"] andColor:[UIColor blackColor]];
}
What this essentially does is draw the image you want twice, once in the colour of the border (slightly scaled) and once with the normal colour. Your mileage may vary depending on the image.
I am trying to create a custom view to draw a music CD with the album artwork on the CD.
Currently I use core graphics to draw two circular disks inside the drawRect method. The inner disk's colour is set to the background colour of the view to make it look like the inner circle is a hollow circle.
My problem is that I can't draw the UIImage inside the the circular bezier path to get the result that I need.
Here's my drawRect code:
// Draw a CD like view using three drawing operations:
// 1st draw the main disk
// 2nd draw the image if it is set
// 3rd draw the inner disk over the image so that it looks like it is a hollow disk with the image painted on the disk.
// Note:) This view expects its aspect ratio to be set as 1:1
- (void)drawRect:(CGRect)rect
{
// Draw the main Circular CD disk
UIBezierPath* outerRing = [UIBezierPath bezierPathWithOvalInRect:self.bounds];
[self.mainColor setFill];
[outerRing fill];
[self.outerRingColor setStroke];
[outerRing stroke];
// Draw the album image if it is set
if(self.artwork){
[self.artwork drawInRect:self.bounds blendMode:kCGBlendModeNormal alpha:1.0];
}
// now draw another smaller disk inside
// this will be a percentage smaller than the whole disk's bounds and will be centered inside it
CGFloat sidePaddingCoord = ((1.0f - INNER_DISK_SIZE_PERCENTAGE) / 2) * self.bounds.size.height;
CGFloat side = INNER_DISK_SIZE_PERCENTAGE * self.bounds.size.width;
UIBezierPath* innerRing = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(sidePaddingCoord, sidePaddingCoord, side, side)];
[self.innerRingColor setFill];
[innerRing fill];
[innerRing stroke];
}
This fills the image as a square. I want the image to be clipped inside the outerRing bezier path so that it looks like a music CD.
I'm sure there is a way to use core graphics to achieve something else besides using the image's drawInRect method. Is there any way to 'clip' the image inside the circular bezier path or only draw inside the bezier path?
I've read really great posts by rob mayoff, UIBezierPath Subtract Path, iOS UIImage clip to paths, many thanks.
Here is an adoption of his code. Keep your creation code of outerRing and innerRing, add them to an array named paths.
[self.paths addObject:outerRing];
[self.paths addObject:innerRing];
Then use this help method.
- (UIImage *)maskedImage
{
CGRect rect = CGRectZero;
rect.size = self.originalImage.size;
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0);
UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectInfinite];
[clipPath appendPath:self.paths[1]];
clipPath.usesEvenOddFillRule = YES;
CGContextSaveGState(UIGraphicsGetCurrentContext()); {
[clipPath addClip];
[[UIColor orangeColor] setFill];
[self.paths[0] fill];
} CGContextRestoreGState(UIGraphicsGetCurrentContext());
UIImage *mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); {
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage);
[self.originalImage drawAtPoint:CGPointZero];
}
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return maskedImage;
}
In drawRect,
UIImage *maskedImage = [self maskedImage];
[maskedImage drawInRect:self.bounds blendMode:kCGBlendModeNormal alpha:1.0];
Try below code:
UIImageView *userImageView= [[UIImageView alloc] initWithFrame:CGRectMake(85, 55.0, 90, 90)];
userImageView.layer.cornerRadius = 90/2;
userImageView.clipsToBounds = YES;
userImageView.layer.borderWidth = 1.0f;
userImageView.layer.borderColor = [UIColor blueColor];
[self.view addSubview:userImageView]; //or add it to the view you want
UIImage *userImage = [UIImage imageNamed:#"<image you want to set>"];
userImageView= [[UIImageView alloc] initWithFrame:CGRectMake(90, 60.0, 80, 80)];
userImageView.image = userImage;
userImageView.layer.cornerRadius = 80/2;
userImageView.clipsToBounds = YES;
userImageView.layer.borderWidth = 1.0f;
userImageView.layer.borderColor = [UIColor blueColor;
[self.view addSubview:userImageView]; //or add it to the view you want
I have a UIView that have a CALayer mask:
// Getting the right mask image
UIImage *myimage = [UIImage imageNamed:[NSString stringWithFormat:#"img%d", imageIndex]];
// Scaling image to fit UIView
UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0);
[myimage drawInRect:self.bounds];
myimage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.layer setMasksToBounds:YES];
// Setting mask
UIImage *_maskingImage = myimage;
CALayer *_maskingLayer = [CALayer layer];
_maskingLayer.frame = self.bounds;
[_maskingLayer setContents:(id)[_maskingImage CGImage]];
[self.layer setMask:_maskingLayer];
When the user later short press (UILongPressGestureRecognizer *) the UIView I just want a action to happend if the user taps in the UIView layer mask (for now containsPoint always returns YES):
// Object is a UIView
CALayer *layer = [Object.layer mask];
// Sender is a UILongPressGestureRecognizer
location2 = [sender locationInView:Object];
// The position is correct
NSLog(#"%#", NSStringFromCGPoint(location2));
if ([layer containsPoint:location2]){
NSLog(#"HELLO WORLD!");
return;
}
Help, please?
My book gives two suggestions. If you know the bounding path of the mask drawing, you can use CGPathContainsPoint. Or, if the layer in question is transparent on the outside and opaque on the inside, you can examine the pixel at the tapped location to see if it is transparent or not.
http://www.apeth.com/iOSBook/ch18.html#_hit_testing
Scroll down to the section "Hit-testing for drawings".
I am trying to draw some circles inside a UIImageView with a specific image. This is what I was trying to do:
UIGraphicsBeginImageContext(self.view.bounds.size);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(contextRef, 2.0);
CGContextSetStrokeColorWithColor(contextRef, [color CGColor]);
CGRect circlePoint = (CGRectMake(coordsFinal.x, coordsFinal.y, 50.0, 50.0));
CGContextStrokeEllipseInRect(contextRef, circlePoint);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[photoView addSubview:image];
The circle is drawn fine, but I would like the PhotoView to act as a mask to it. So if for example I move the UIImageView out of the UIView using an animation, I would like the circle to move with it. Important is the fact that the coordinates are relative to the whole screen.
Use Core Animation's shape layer instead.
CAShapeLayer *circleLayer = [CAShapeLayer layer];
// Give the layer the same bounds as your image view
[circleLayer setBounds:CGRectMake(0.0f, 0.0f, [photoView bounds].size.width,
[photoView bounds].size.height)];
// Position the circle anywhere you like, but this will center it
// In the parent layer, which will be your image view's root layer
[circleLayer setPosition:CGPointMake([photoView bounds].size.width/2.0f,
[photoView bounds].size.height/2.0f)];
// Create a circle path.
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:
CGRectMake(0.0f, 0.0f, 50.0f, 50.0f)];
// Set the path on the layer
[circleLayer setPath:[path CGPath]];
// Set the stroke color
[circleLayer setStrokeColor:[[UIColor redColor] CGColor]];
// Set the stroke line width
[circleLayer setLineWidth:2.0f];
// Add the sublayer to the image view's layer tree
[[photoView layer] addSublayer:circleLayer];
Now, if you animate the UIImageView that contains this layer, the layer will move with it since it is a child layer. And there is now no need to override drawRect:.
self.myPath=[UIBezierPath bezierPathWithArcCenter:center
radius:200
startAngle:0
endAngle:180
clockwise:YES];
(This much I was able to get up running with some web searching).
I have this path. Now I want to fill the reverse of this path, so leaving this portion and filling everything else. How can I finish the coding? I don't have much info on this.
The problem
The area it is showing after using Cemal Answer previously it only showed a circle with red stroke.
Edit
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.punchedOutPath =
[UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 400, 400)];
self.fillColor = [UIColor redColor];
self.alpha = 0.8;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[[self fillColor] set];
UIRectFill(rect);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
[[self punchedOutPath] fill];
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
}
Use bezierPathByReversingPath. From the docs (iOS 6.0+ only):
Creates and returns a new bezier path object with the reversed contents of the current path.
so to reverse your path, you'd just:
UIBezierPath* aPath = [UIBezierPath bezierPathWithArcCenter:center
radius:200
startAngle:0
endAngle:180
clockwise:YES];
self.myPath = [aPath bezierPathByReversingPath];
Here's an alternative that doesn't require reversing the path at all.
You have a portion of a view you essentially want to "clip out":
Let's say you want the white area to be [UIColor whiteColor] with 75% alpha. Here's how you do it quickly:
You create a new UIView subclass.
This view has two properties:
#property (retain) UIColor *fillColor;
#property (retain) UIBezierPath *punchedOutPath;
You override its -drawRect: method to do this:
- (void)drawRect:(CGRect)rect {
[[self fillColor] set];
UIRectFill(rect);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
[[self punchedOutPath] fill];
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
}
There's a caveat here: The fillColor of the view must not include the alpha component. So in your case, you'd want that to just be [UIColor whiteColor]. You then apply the alpha bit yourself by calling [myView setAlpha:0.75].
What's going on here: This is using a blend mode called "Destination Out". Mathematically it's defined as R = D*(1 - Sa), but in layman's terms it means "Destination image wherever destination image is opaque but source image is transparent, and transparent elsewhere."
So it's going to use the destination (i.e., what's already in the context) wherever the new stuff is transparent (i.e. outside of the bezier path), and then where the bezier path would be opaque, that stuff is going to become transparent. However, the destination stuff must already be opaque. If it's not opaque, the blending doesn't do what you want. This is why you have to provide an opaque UIColor and then do any transparency you want with the view directly.
I ran this myself, with these circumstances:
the window has a [UIColor greenColor] background
the fillColor is white
the punchedOutPath is a oval that's inset 10 points from the edges of the view.
the view has an alpha of 0.75
With the code above, I get this:
The interior is pure green, and the outside has the semi-transparent overlay.
Update
If your covering is an image, then you'll need to create a new image. But the principle is the same:
UIImage* ImageByPunchingPathOutOfImage(UIImage *image, UIBezierPath *path) {
UIGraphicsBeginImageContextWithOptions([image size], YES, [image scale]);
[image drawAtPoint:CGPointZero];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
[path fill];
UIImage *final = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return final;
}
You would then take the result of this function and put it into a UIImageView.
You can put this into a single screen app into the view controller: It will make a yellow background view and a blue layer on top of it that has an oval region cut out by a mask.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// create a yellow background
UIView *bg = [[UIView alloc] initWithFrame:self.view.bounds];
bg.backgroundColor = [UIColor yellowColor];
[self.view addSubview:bg];
// create the mask that will be applied to the layer on top of the
// yellow background
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.frame = self.view.frame;
// create the paths that define the mask
UIBezierPath *maskLayerPath = [UIBezierPath bezierPath];
[maskLayerPath appendPath:[UIBezierPath bezierPathWithRect:CGRectInset(self.view.bounds, 20, 20)]];
// here you can play around with paths :)
// [maskLayerPath appendPath:[UIBezierPath bezierPathWithOvalInRect:(CGRect){{80, 80}, {140, 190}}]];
[maskLayerPath appendPath:[UIBezierPath bezierPathWithOvalInRect:(CGRect){{100, 100}, {100, 150}}]];
maskLayer.path = maskLayerPath.CGPath;
// create the layer on top of the yellow background
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = self.view.layer.bounds;
imageLayer.backgroundColor = [[UIColor blueColor] CGColor];
// apply the mask to the layer
imageLayer.mask = maskLayer;
[self.view.layer addSublayer:imageLayer];
}
this might answer this question as well: UIBezierPath Subtract Path
I have two solution for you.
Draw this path on a CALayer. And use that CALayer as a mask layer for you actual CALayer.
Draw a rectangle with the sizes of you frame before adding arc.
UIBezierPath *path = [UIBezierPath bezierPathWithRect:view.frame];
[path addArcWithCenter:center
radius:200
startAngle:0
endAngle:2*M_PI
clockwise:YES];
I would use second solution. :)