Image mask without transparant part - iphone

I've been looking into this for a while now.
I'm trying to put a gray image over a cup
Currently i'm stretching an uiimageview with a grey background to the amount the slider has gone.
-(void) sliderChanged:(CGFloat) value{
drinksView.grayArea.frame = CGRectMake( 0 , -value ,372 ,value);
I know this is very dirty ,not what i want...
What i want is that the grey part only covers the part where there is a cup (e.g the part where the image is not transparant). The image of the cup just has a transparant background
Does anybody have an idea of how to achieve this ? i'm a noob with masks and many tutorials have led me nowhere and i don't even know if it's possible.
P.S: drawing a path around the cup is not possible because the cup image can change to a glass

The easiest way I can think of is to use a masked CALayer. This is what you need to do:
Instead of using a UIImageView, use a CALayer with gray background as your overlay. Your sliderChanged: method would remain untouched, except that drinksView.grayArea would be a layer instead of a view.
So far the effect will be exactly the same as before. Now, you need to set the grayArea's mask. Do the following:
CALayer * maskLayer = [CALayer new];
maskLayer.contents = myCupImage.CGImage;
grayArea.mask = maskLayer;
I think by default the layer will stretch the content as the scale is changed. We don't want that. You can fix this by setting the layer's contentsGravity to, say, kCAGravityTop.
That should do what you want.
One caveat: I'm not quite sure how masks cope with changing content gravity. If you have issues on that front, you can fixed it by adding a container layer:
Set a fixed frame for grayArea (equal to the size of the cup image).
Instead of adding grayArea directly, introduce a container layer for it:
CALayer * container = [CALayer new];
container.masksToBounds = YES;
[container addSublayer:grayArea];
[drinksView.layer addSublayer:container];
In your sliderChanged:, change the frame of container instead of the grayArea.
Hope this works.

First of all you will need to get familiar with this method
// masks the item based on the MaskImage
- (UIImage*) itemMask : (UIImage*)image withMask:(UIImage*)maskImage
{
UIImage* afterMasking = nil;
CGImageRef maskRef = maskImage.CGImage;
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef), CGImageGetBitsPerComponent(maskRef), CGImageGetBitsPerPixel(maskRef), CGImageGetBytesPerRow(maskRef), CGImageGetDataProvider(maskRef), NULL, false);
CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
CFRelease(mask);
afterMasking = [UIImage imageWithCGImage:masked];
CFRelease(masked);
return afterMasking;
}
What that does is that you feed it your Cup image and the mask Image together and it will mask your cup image. It will only mask your cup image and nothing underneath so you don't have to worry.
The problem you have is that the grey box resizes. How I would approach this is to crop the mask according to the slider value. So make a black box and change it size how you do with the grayBG BEFORE you feed it through the method. That should be quick to do so so I won't elaborate but at the end you will have something like this. Pardon the half elaborated graphics

drinksView.grayLayer = [CALayer new];
drinksView.grayLayer.frame = CGRectMake(0, 0, 372, 0);
drinksView.grayLayer.contentsGravity = kCAGravityTop;
CALayer * topLayer = [CALayer new];
topLayer.frame = CGRectMake(0, 0, 372, 367);
UIImage * grayImage = [UIImage imageNamed:#"grayDrink.png"];
topLayer.contents = (id) grayImage.CGImage;
CALayer * maskLayer = [CALayer new];
maskLayer.contents = (id)cupImage.CGImage;
maskLayer.masksToBounds = YES;
maskLayer.bounds = CGRectMake(0, 0, 372, 367);
maskLayer.position = CGPointMake(186,184);
[topLayer setMask:maskLayer];
[drinksView.grayLayer addSublayer:topLayer];
[drinksView.grayLayer setMasksToBounds:YES];
[[drinksView layer]addSublayer:drinksView.grayLayer];

Related

divide image into two parts using divider

I'm working on one app where I need to divide a image into two part using a red line.
left part for labels
right part for prices
Question 1.
How can I draw a red line on image?
Question 2.
How can I divide image to two parts using red line ?( red line position is not fixed. user can move the position wherever it want)
Question 3.
How can I get line current position and how can I use that position two divide image
Thanks in advance
I would approach this in somewhat the same manner as koray was suggesting:
1) I am assuming that your above image/view is going to be managed by a view controller, which I will call ImageSeperatorViewController from here on.
Inside of ImageSeperatorViewController, insert koray's code in the -(void) viewDidLoad{} method. Make sure you change the imageToSplit variable to be an UIImageView instead of a plain UIView.
2) Next, I assume that you know how to detect user gestures. You will detect these gestures, and determine if the user has selected the view (i.e. bar in koray's code). Once you have determined if the user has selected bar, just update its origin's X position with the touch position.
CGRect barFrame = bar.frame;
barFrame.origin.x = *X location of the users touch*
bar.frame = barFrame;
3) For cropping, I would not use github.com/bilalmughal/NLImageCropper, it will not do what you need to do.
Try this on for size:
Header:
#interface UIImage (ImageDivider)
- (UIImage*)imageWithDividerAt:(CGFloat)position width:(CGFloat)width color:(UIColor*)color;
- (UIImage*)imageWithDividerAt:(CGFloat)position patternImage:(UIImage*)patternImage;
- (NSArray*)imagesBySlicingAt:(CGFloat)position;
#end
Implementation:
#implementation UIImage (ImageDivider)
- (UIImage*)imageWithDividerAt:(CGFloat)position patternImage:(UIImage*)patternImage
{
//pattern image
UIColor *patternColor = [UIColor colorWithPatternImage:patternImage];
CGFloat width = patternImage.size.width;
//set up context
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//set the fill color from the pattern image color
CGContextSetFillColorWithColor(context, patternColor.CGColor);
//this is your divider's area
CGRect dividerRect = CGRectMake(position - (width / 2.0f), 0, width, self.size.height);
//the joy of image color patterns being based on 0,0 origin! must set phase
CGContextSetPatternPhase(context, CGSizeMake(dividerRect.origin.x, 0));
//fill the divider rect with the repeating pattern from the image
CGContextFillRect(context, dividerRect);
//get your new image and viola!
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage*)imageWithDividerAt:(CGFloat)position width:(CGFloat)width color:(UIColor *)color
{
//set up context
UIGraphicsBeginImageContext(self.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//set the fill color for your divider
CGContextSetFillColorWithColor(context, color.CGColor);
//this is your divider's area
CGRect dividerRect = CGRectMake(position - (width / 2.0f), 0, width, self.size.height);
//fill the divider's rect with the provided color
CGContextFillRect(context, dividerRect);
//get your new image and viola!
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (NSArray*)imagesBySlicingAt:(CGFloat)position
{
NSMutableArray *slices = [NSMutableArray array];
//first image
{
//context!
UIGraphicsBeginImageContext(CGSizeMake(position, self.size.height));
//draw the existing image into the context
[self drawAtPoint:CGPointZero];
//get your new image and viola!
[slices addObject:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
}
//second
{
//context!
UIGraphicsBeginImageContext(CGSizeMake(self.size.width - position, self.size.height));
//draw the existing image into the context
[self drawAtPoint:CGPointMake(-position, 0)];
//get your new image and viola!
[slices addObject:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
}
return slices;
}
The concept is simple - you want an image with the divider drawn over it. You could just overlay a view, or override drawRect:, or any number of any solutions. I'd rather give you this category. It just uses some quick Core Graphics calls to generate an image with your desired divider, be it pattern image or color, at the specified position. If you want support for horizontal dividers as well, it is rather trivial to modify this as such. Bonus: You can use a tiled image as your divider!
Now to answer your primary question. Using the category is rather self explanatory - just call one of the two methods on your source background to generate one with the divider, and then apply that image rather than the original source image.
Now, the second question is simple - when the divider has been moved, regenerate the image based on the new divider position. This is actually a relatively inefficient way of doing it, but this ought to be lightweight enough for your purposes as well as only being an issue when moving the divider. Premature optimization is just as much a sin.
Third question is also simple - call imagesBySlicingAt: - it will return an array of two images, as generated by slicing through the image at the provided position. Use them as you wish.
This code has been tested to be functional. I strongly suggest that you fiddle around with it, not for any purpose of utility, but to better understand the mechanisms used so that next time, you can be on the answering side of things
For Crop you can try this,
UIImage *image = [UIImage imageNamed:#"yourImage.png"];
CGImageRef tmpImgRef = image.CGImage;
CGImageRef topImgRef = CGImageCreateWithImageInRect(tmpImgRef, CGRectMake(0, 0, image.size.width, image.size.height / 2.0));
UIImage *topImage = [UIImage imageWithCGImage:topImgRef];
CGImageRelease(topImgRef);
CGImageRef bottomImgRef = CGImageCreateWithImageInRect(tmpImgRef, CGRectMake(0, image.size.height / 2.0, image.size.width, image.size.height / 2.0));
UIImage *bottomImage = [UIImage imageWithCGImage:bottomImgRef];
CGImageRelease(bottomImgRef);
hope this can help you, :)
if you want to draw a line you could just use a UIView with red background and make the height the size of your image and the width around 5 pixels.
UIView *imageToSplit; //the image im trying to split using a red bar
CGRect i = imageToSplit.frame;
int x = i.origin.x + i.size.width/2;
int y = i.origin.y;
int width = 5;
int height = i.size.height;
UIView *bar = [[[UIView alloc] initWithFrame:CGRectMake(x, y, width, height)] autorelease];
bar.backgroundColor = [UIColor redColor];
[self.view addSubview:bar];

iphone - Focus effect (just like UIAlertView)

I know title of my question is so bad, but I don't know how to describe it.
When an UIAlertView pops up, anything else on the screen (except the UIAlertView) becomes a bit darker but can be seen. I call this as Focus effect, because you will know clearly and directly that now the UIAlertView is the focus.
So how can I implement such a focus effect?
thanks
Just add a translucent view below the view you want to "focus" on.
Simple example:
UIView *shieldView = [[[UIView alloc] initWithFrame:myView.bounds] autorelease];
shieldView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.7];
[myView.superview insertSubview:shieldView belowSubview:myView];
UIAlertView actually uses an image with a radial gradient instead of a simple color, in order to highlight the center of the view.
I know this post is a bit old but I thought it might help someone.
Use this code to generate the radial gradient background:
- (UIImage *)radialGradientImage:(CGSize)size start:(float)start end:(float)end centre:(CGPoint)centre radius:(float)radius{
UIGraphicsBeginImageContextWithOptions(size, YES, 1);
size_t count = 2;
CGFloat locations[2] = {0.0, 1.0};
CGFloat components[8] = {start, start, start, 1.0, end, end, end, 1.0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef grad = CGGradientCreateWithColorComponents (colorSpace, components, locations, count);
CGColorSpaceRelease(colorSpace);
CGContextDrawRadialGradient (UIGraphicsGetCurrentContext(), grad, centre, 0, centre, radius, kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(grad);
UIGraphicsEndImageContext();
return image;}
Define gradient in the .h file like so:
UIImageView *gradient;
Call your gradient like so:
- (void)addGradient{
CGSize size = self.view.bounds.size;
CGPoint centre = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
float startColor = 1.0f;
float endColor = 0.0f;
float radius = MIN(self.view.bounds.size.width/4, self.view.bounds.size.height/4);
gradient = [[UIImageView alloc] initWithImage:[self radialGradientImage:size
start:startColor
end:endColor
centre:centre
radius:radius]];
[gradient setBackgroundColor:[UIColor clearColor]];
[gradient setUserInteractionEnabled:YES];
[gradient setAlpha:0.6f];
[self.view addSubview:gradient];}
UIAlertView works like this. It fades in an alpha mask image to dim out the background. Once that animation is finished it starts the "bounce in" animation of the dialog.
So to reproduce it you need first to generate an alpha mask with a "bright spot" where your dialog will end up and fade that in. Then use a (few) frame animation(s) to get the bounce effect.
More info here: Creating a Pop animation similar to the presentation of UIAlertView
To make it better than "not good" you could ...
create a UIView in a nib (easiest if the part of your code where you need the effect is already utilising a nib) and then add a translucent graphic (with a 'focus' effect) to that view.
connect the UIView in the nib to an IBOutlet
fade in the graphic using an animation into view hierarchy (omz example shows this)

How to I rotate UIImageView by 90 degrees inside a UIScrollView with correct image size and scrolling?

I have an image inside an UIImageView which is within a UIScrollView. What I want to do is rotate this image 90 degrees so that it is in landscape by default, and set the initial zoom of the image so that the entire image fits into the scrollview and then allow it to be zoomed up to 100% and back down to minimum zoom again.
This is what I have so far:
self.imageView.transform = CGAffineTransformMakeRotation(-M_PI/2);
float minimumScale = scrollView.frame.size.width / self.imageView.frame.size.width;
scrollView.minimumZoomScale = minimumScale;
scrollView.zoomScale = minimumScale;
scrollView.contentSize = CGSizeMake(self.imageView.frame.size.height,self.imageView.frame.size.width);
The problem is that if I set the transform, nothing shows up in the scrollview. However if I commented out the transform, everything works except the image is not in the landscape orientation that I want it to be!
If I apply the transform and remove the code that sets the minimumZoomScale and zoomScale properties, then the image shows up in the correct orientation, however with the incorrect zoomScale and seems like the contentSize property isn't set correctly either - since the doesn't scroll to the edge of the image in the left/right direction, however does top and bottom but much over the edge.
NB: image is being loaded from a URL
Maybe rotating the image itself fits your needs:
UIImage* rotateUIImage(const UIImage* src, float angleDegrees) {
UIView* rotatedViewBox = [[UIView alloc] initWithFrame: CGRectMake(0, 0, src.size.width, src.size.height)];
float angleRadians = angleDegrees * ((float)M_PI / 180.0f);
CGAffineTransform t = CGAffineTransformMakeRotation(angleRadians);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
[rotatedViewBox release];
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
CGContextRotateCTM(bitmap, angleRadians);
CGContextScaleCTM(bitmap, 1.0, -1.0);
CGContextDrawImage(bitmap, CGRectMake(-src.size.width / 2, -src.size.height / 2, src.size.width, src.size.height), [src CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
I believe the easiest way (and thread safe too) is to do:
//assume that the image is loaded in landscape mode from disk
UIImage * LandscapeImage = [UIImage imageNamed: imgname];
UIImage * PortraitImage = [[UIImage alloc] initWithCGImage: LandscapeImage.CGImage
scale: 1.0
orientation: UIImageOrientationLeft];
Any calculations that you do based on the imageView's frame should probably be done before you apply any transformations to it. But I would actually suggest doing those calculations based on the size of the UIImage, not the UIImageView. Then set both the UIImageView's frame and the UIScrollView's contentSize based on that.
Max's suggestion is a good one, although with a larger image it could be a performance killer. Are you displaying this image from your app's resources? If so, why not just rotate the images before you even build the app?
There's a much easier solution that is also faster, just do this:
- (void) imageRotateTapped:(id)sender
{
[UIView animateWithDuration:0.33f animations:^()
{
self.imageView.transform = CGAffineTransformMakeRotation(RADIANS(self.rotateDegrees += 90.0f));
self.imageView.frame = self.imageView.superview.bounds; // change this to whatever rect you want
}];
}
When the user is done, you will need to actually create a new rotated image, but that is very easy to do.
I was using the accepted answer for a while until we noticed that non-square rotations based on images taken directly from the camera seemed stretched (they were rotated as desired, just the frame width/height wasn't adjusted).
Great explanation/post here from Trevor: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
In the end, it was a very simple import of Trevor's code which uses categories to add a resizedImage:interpoationQuality method to UIImage. So yeah, user beware, if it still works for you, great. But if it doesn't, I'd take a look at the library instead.

Is this kind of masking possible with UIImage or CGImage API in iOS

I have an UIImage with some text and would like to apply pattern UIImage as masking. Is this possible ?
I understand that with UILabel we can get this kind of gradient using CAGradientLayer. But can this be done if the source is an UIImage ?
The image may have some symbols/pictures etc other than regular characters and hence UIImage. Also i could reuse the image by applying different masking pattern depending on the context.
is this possible ?
Appreciate your help.
EDIT: Thanks for all your answers.
I understand applying the gradient to a text label or creating an image that has text.
But my goal is to get this.--> Click here
i.e. i have a png with some drawing like a flower with transparent background. I want to apply the gradient to the object inside that picture at runtime with a gradient.png as shown in the picture above. Is that possible with masking ?
Thanks
Looks like you should be able to use CGImageMaskCreate:
- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
CGImageRef maskRef = maskImage.CGImage;
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);
CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
return [UIImage imageWithCGImage:masked];
}
For a longer discussion check out the comment thread here:
http://iosdevelopertips.com/cocoa/how-to-mask-an-image.html
Yes, it is :)
textField.textColor = [UIColor colorWithPatternImage: [UIImage imageNamed:#"rainbowGradient.png"]];
If you want heavy control, Jason Whyne's idea might work. But I like this one, because it's about 8 lines shorter.
Here's just another way to draw an image of text masking something. It's based on kCGBlendModeSourceIn blending mode: you draw text on a clear background and then draw the fill all over the place.
NSString *theString = ...;
UIFont *theFont = ...;
CGSize stringSize = [theString sizeWithFont:theFont];
// The background must be clear (fully transparent), hence NO as the 2nd argument
UIGraphicsBeginImageContextWithOptions(stringSize, NO, 0);
[theString drawAtPoint:CGPointZero withFont:theFont];
// This effectively colorizes the image. Use a pattern color...
[patternColor set];
UIRectFillUsingBlendMode(CGRectMake(0, 0, stringSize.width, stringSize.height), kCGBlendModeSourceIn);
// ... or an image:
[patternImage drawInRect:CGRectMake(0, 0, stringSize.width, stringSize.height) blendMode:kCGBlendModeSourceIn alpha:1.0f];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
sample using mask is fine and works well, but You are leaking.
CGImageMaskCreate and CGImageCreateWithMask do allocate (following "create" -> retain rule)
so You should release mask & image after using:
CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
UIImage * result = [UIImage imageWithCGImage:masked];
CGImageRelease(mask);
CGImageRelease(masked);
return result;
As per ADC docs:
...
Return Value
A Quartz bitmap image mask. You are responsible for releasing this object by calling CGImageRelease.

How do achieve a frame around image

I like the way this (http://shakeitphoto.com/) application puts a border around the image.. i would like to do something similar in my application but not sure how should I go about doing it.
Any ideas on how given a UIImage can I wrap a frame around it?
From that website, it appears you want a border with a shadow. There's 2 reasonable options, 3 if you don't care about the shadow.
If you don't care about the shadow, you can just do something like
#import <QuartzCore/QuartzCore.h> // this should be at the top
// inside your view layout code
myImageView.layer.borderColor = [UIColor whiteColor].CGColor
myImageView.layer.borderWidth = 5;
This will give you a 5-pixel white border inset into the view, layered on top of the view's contents (e.g. the image). What it won't give you is a shadow. If you want the shadow, there's 2 other options.
You could just create an image that includes the border and the shadow, and nothing else. Just make everything else alpha-transparent. Then you can simply layer this image on top of the one you want to display (either with 2 imageviews, or by creating a third image out of the 2). This should work fine, but it won't scale to different image sizes. In the case of the linked app, the image size is always the same so they could be using this.
The other option is to simply draw the border and shadow on top of your image in a new image. Here's a bit of sample code that will do this - it creates a new image the same size as your original, but with a white, shadowed border:
- (UIImage *)borderedImage:(UIImage *)image {
// the following NO means the new image has an alpha channel
// If you know the source image is fully-opaque, you may want to set that to YES
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
[image drawAtPoint:CGPointZero];
CGContextRef ctx = UIGraphicsGetCurrentContext();
const CGFloat shadowRadius = 5;
CGContextSetShadowWithColor(ctx, 0, shadowRadius, [UIColor blackColor].CGColor);
[[UIColor whiteColor] set];
CGRect rect = (CGRect){CGPointZero, image.size};
const CGFloat frameWidth = 5;
rect = CGRectInset(rect, frameWidth / 2.0f, frameWidth / 2.0f);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:rect];
path.lineWidth = frameWidth;
[path stroke];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// note: getting the new image this way throws away the orientation data from the original
// You could create a third image by doing something like
// newImage = [UIImage imageWithCGImage:newImage.CGImage scale:newImage.scale orientation:image.orientation]
// but I am unsure as to how orientation actually affects rendering (if at all)
UIGraphicsEndImageContext();
return newImage;
}
(note: this code has not been compiled and could contain bugs)