How to create a flexible frame for photo in iPhone? - iphone

I try to create a flexible frame for in my iPhone app with some small pictures. CustomView inherit UIView and override it's setFrame: method. In setFrame:, I try to call [self setNeedsDisplay]; Every time I scale the photo, this frame really display and changed, but something does not work very well. Code and effect below:
//rotate to get mirror image of originImage, and isHorization means the rotation direction
- (UIImage*)fetchMirrorImage:(UIImage*)originImage direction:(BOOL)isHorization{
CGSize imageSize = originImage.size;
UIGraphicsBeginImageContext(imageSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform transform = CGAffineTransformIdentity;
if (isHorization) {
transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
}else {
transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
}
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -imageSize.height);
CGContextConcatCTM(context, transform);
CGContextDrawImage(context, CGRectMake(0, 0, imageSize.width, imageSize.height), originImage.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage*)fetchPattern:(PatternType)pattern{
if (!self.patternImage) {
return nil;
}
UIImage *tmpPattern = nil;
CGRect fetchRect = CGRectZero;
CGSize imageSize = self.patternImage.size;
switch (pattern) {
case kTopPattern:
fetchRect = CGRectMake(self.insetSize.width, 0, imageSize.width-self.insetSize.width, self.insetSize.height);
tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];
break;
case kTopRightPattern:
fetchRect = CGRectMake(0, 0, self.insetSize.width, self.insetSize.height);
tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];
break;
case kRightPattern:
break;
case kRightBottomPattern:
break;
case kBottomPattern:
break;
case kLeftBottomPattern:
break;
case kLeftPattern:
fetchRect = CGRectMake(0, self.insetSize.height, self.insetSize.width, imageSize.height-self.insetSize.height);
tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];
break;
case kLeftTopPattern:
fetchRect = CGRectMake(0, 0, self.insetSize.width, self.insetSize.height);
tmpPattern = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(self.patternImage.CGImage, fetchRect)];
break;
default:
break;
}
return tmpPattern;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
if (self.patternImage == nil) {
return;
}
// Drawing code.
UIImage *conLeftImage = [self fetchPattern:kLeftTopPattern];
[conLeftImage drawAtPoint:CGPointMake(0, 0)];
UIImage *topImage = [self fetchPattern:kTopPattern];
[topImage drawAsPatternInRect:CGRectMake(self.insetSize.width, 0, self.frame.size.width-self.insetSize.width*2, self.insetSize.height)];
UIImage *leftImage = [self fetchPattern:kLeftPattern];
[leftImage drawAsPatternInRect:CGRectMake(0, self.insetSize.height, self.insetSize.width, self.frame.size.height-self.insetSize.height*2)];
UIImage *conRightImage = [self fetchMirrorImage:conLeftImage direction:YES];
[conRightImage drawAtPoint:CGPointMake(self.frame.size.width-self.insetSize.width, 0)];
UIImage *rightImage = [self fetchMirrorImage:leftImage direction:YES];
CGRect rectRight = CGRectMake(self.frame.size.width-self.insetSize.width, self.insetSize.height, self.insetSize.width, self.frame.size.height-self.insetSize.height*2);
[rightImage drawAsPatternInRect:rectRight];
UIImage *botRightImage = [self fetchMirrorImage:conRightImage direction:NO];
[botRightImage drawAtPoint:CGPointMake(self.frame.size.width-self.insetSize.width, self.frame.size.height-self.insetSize.height)];
UIImage *bottomImage = [self fetchMirrorImage:topImage direction:NO];
CGRect bottomRect = CGRectMake(self.insetSize.width, self.frame.size.height-self.insetSize.height, self.frame.size.width-self.insetSize.width*2, self.insetSize.height);
[bottomImage drawAsPatternInRect:bottomRect];
UIImage *botLeftImage = [self fetchMirrorImage:conLeftImage direction:NO];
[botLeftImage drawAtPoint:CGPointMake(0, self.frame.size.height-self.insetSize.height)];
[super drawRect:rect];
}
- (void)setFrame:(CGRect)frame{
[super setFrame:frame];
[self setNeedsDisplay];
}

Yeah, I make it!!!
The only thing I need to do is set the phase of current context.
CGContextSetPatternPhase(context, CGSizeMake(x, y));
x, y are the start point where I want the pattern to be drawn.
I just made a mistake to use pattern. If I don't set the phase of current context, It start fill the pattern from the left top of context(0,0) every time.
CGColorRef color = NULL;
UIImage *leftImage = [UIImage imageNamed:#"like_left.png"];
color = [UIColor colorWithPatternImage:leftImage].CGColor;
CGContextSetFillColorWithColor(context, color);
CGContextFillRect(context, CGRectMake(0, 0, FRAME_WIDTH, rect.size.height));
UIImage *topImage = [UIImage imageNamed:#"like_top.png"];
color = [UIColor colorWithPatternImage:topImage].CGColor;
CGContextSetFillColorWithColor(context, color);
CGContextFillRect(context, CGRectMake(0, 0, rect.size.width,FRAME_WIDTH));
UIImage *bottomImage = [UIImage imageNamed:#"like_bom.png"];
CGContextSetPatternPhase(context, CGSizeMake(0, rect.size.height - FRAME_WIDTH));
color = [UIColor colorWithPatternImage:bottomImage].CGColor;
CGContextSetFillColorWithColor(context, color);
CGContextFillRect(context, CGRectMake(0, rect.size.height - FRAME_WIDTH, rect.size.width, FRAME_WIDTH));
UIImage *rightImage = [UIImage imageNamed:#"like_right.png"];
CGContextSetPatternPhase(context, CGSizeMake(rect.size.width - FRAME_WIDTH, 0));
color = [UIColor colorWithPatternImage:rightImage].CGColor;
CGContextSetFillColorWithColor(context, color);
CGContextFillRect(context, CGRectMake(rect.size.width - FRAME_WIDTH, 0, FRAME_WIDTH, rect.size.height));

First, I'll say this:
I don't have a real answer right now. But, I have a hint .. I think it'll be useful.
According to the tutorial posted by Larmarche, you can customize the borders of a UIView easily by overriding drawRect:. And, if you scroll down to the comments below (in the tutorial website), you can see the following comment:
I discovered one other glitch. When you change the RoundRect's bounds,
it will not be updated -- so the corners sometimes get all screwy
(because they're scaled automatically).
The quick fix is to add the following to both init methods:
self.contentMode = UIViewContentModeRedraw;
Setting the contentMode to Redraw saves you the trouble of calling setNeedsDisplay. You can easily change the frame of the UIView, instantly or animate it, and the UIView will redraw automatically..
I will try to take a better look at your problem later .. I'll try to actually implement it.

Related

UIView: Draw at double the size

I have a UIView with a dynamically generated graphic context. Its normal size is 100x100, but it can be scaled up to 200x200, so I want its size to be actually 200x200 in order to appear nicely and not blurred however it is displayed.
How can always draw it 200x200 in drawRect, being it displayed at a size of 100 or at a size of 200? If I'm showing it at 100x100, the rect passed to drawRect is smaller than the area I need to draw into.
My code:
- (void) drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
// prevent the drawings to be flipped
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height), [UIImage imageNamed:#"actionBg.png"].CGImage);
// generate the overlay
if ([self isActive] == NO && self.fullDelay != 0) { // TODO: remove fullDelay check!
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
CGContextRef overlayCtx = UIGraphicsGetCurrentContext();
int segmentSize = (rect.size.height / [self fullDelay]);
for (int i=0; i<[self fullDelay]; i++) {
float alpha = 0.9 - (([self fullDelay] * 0.1) - (i * 0.1));
[[UIColor colorWithRed:120.0/255.0 green:14.0/255.0 blue:14.0/255.0 alpha:alpha] setFill];
if (currentDelay > i) {
CGRect r = CGRectMake(0, i * segmentSize, rect.size.width, segmentSize);
CGContextFillRect(overlayCtx, r);
}
[[UIColor colorWithRed:1 green:1 blue:1 alpha:0.3] setFill];
CGRect line = CGRectMake(0, (i * segmentSize) + segmentSize - 1 , rect.size.width, 1);
CGContextFillRect(overlayCtx, line);
}
UIImage *overlay = UIGraphicsGetImageFromCurrentImageContext();
UIImage *overlayMasked = [TDUtilities maskImage:overlay withMask:[UIImage imageNamed:#"actionMask.png"]];
// prevent the drawings to be flipped
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
// put the overlay
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextDrawImage(ctx, rect, overlayMasked.CGImage);
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
UIGraphicsEndImageContext();
}
// prevent the drawings to be flipped
CGContextTranslateCTM(ctx, 0, rect.size.height);
CGContextScaleCTM(ctx, 1.0, -1.0);
// draw the delay symbol
CGContextDrawImage(ctx, rect, [UIImage imageNamed:#"delaySymbol.png"].CGImage);
CGContextSetAlpha(ctx, 0.8);
// draw the symbol
NSString *imgName = [K_ACTION_IMAGES objectAtIndex:[self actionType]];
CGContextDrawImage(ctx, rect, [UIImage imageNamed:imgName].CGImage);
CGContextSetAlpha(ctx, 1);
// draw the delay number
imgName = [NSString stringWithFormat:#"%d.png", [self fullDelay]];
CGContextDrawImage(ctx, rect, [UIImage imageNamed:imgName].CGImage);
// draw the priority number
imgName = [NSString stringWithFormat:#"%d.png", [self actionType]];
CGContextDrawImage(ctx, CGRectMake(32, 0, rect.size.width, rect.size.height), [UIImage imageNamed:imgName].CGImage);
}
Have you called setNeedsDisplay method for you view after setting its frame

drawRect at double the size

If I override drawRect in order to display an image and place a dinamically-generated overlay on it (see code), whenever I scale up the image it is drawn in a very blurry way as the result of the scaling.
The image is composed of two pieces, an image drawn from a png (whose original size is 2x the wanted one, so it should not give problems when scaled, but it does) and the other is dinamically generated according to the rect size, so it should also adapt to the current rect size, but it doesn't.
Any help?
- (void) drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextDrawImage(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height), [UIImage imageNamed:#"actionBg.png"].CGImage);
// generate the overlay
if ([self isActive] == NO && self.fullDelay != 0) { // TODO: remove fullDelay check!
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
CGContextRef overlayCtx = UIGraphicsGetCurrentContext();
int segmentSize = (rect.size.height / [self fullDelay]);
for (int i=0; i<[self fullDelay]; i++) {
float alpha = 0.9 - (([self fullDelay] * 0.1) - (i * 0.1));
[[UIColor colorWithRed:120.0/255.0 green:14.0/255.0 blue:14.0/255.0 alpha:alpha] setFill];
if (currentDelay > i) {
CGRect r = CGRectMake(0, i * segmentSize, rect.size.width, segmentSize);
CGContextFillRect(overlayCtx, r);
}
[[UIColor colorWithRed:1 green:1 blue:1 alpha:0.3] setFill];
CGRect line = CGRectMake(0, (i * segmentSize) + segmentSize - 1 , rect.size.width, 1);
CGContextFillRect(overlayCtx, line);
}
UIImage *overlay = UIGraphicsGetImageFromCurrentImageContext();
UIImage *overlayMasked = [TDUtilities maskImage:overlay withMask:[UIImage imageNamed:#"actionMask.png"]];
// prevent the drawings to be flipped
CGContextTranslateCTM(overlayCtx, 0, rect.size.height);
CGContextScaleCTM(overlayCtx, 1.0, -1.0);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextDrawImage(ctx, rect, overlayMasked.CGImage);
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
UIGraphicsEndImageContext();
}
The problem is that you are drawing overlayMasked as a CGImage with CGContextDrawImage, which knows nothing of scale. Either you will have to double the size yourself manually if you're in a double-scale situation, or you should use UIImage, which knows about scale.

How do you crop an image in iOS

I have a photo app where you can add stickers in one section. When you're finished I want to save the image. Here is the code that I have to do that.
if(UIGraphicsBeginImageContextWithOptions != NULL)
{
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 2.5);
} else {
UIGraphicsBeginImageContext(self.view.frame.size);
}
CGContextRef contextNew=UIGraphicsGetCurrentContext();
[self.view.layer renderInContext:contextNew];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Now the image that gets saved is the full screen of the image, which is fine, but now I need to crop the image and I don't know how. You can see the image at the link below:
http://dl.dropbox.com/u/19130454/Photo%202012-04-09%201%2036%2018%20PM.png
I need to crop:
91px from the left and right
220px from the bottom
Any help would be greatly appreciated. If I haven't explained things clearly, please let me know and I'll do my best to re-explain.
How about something like this
CGRect clippedRect = CGRectMake(self.view.frame.origin.x+91, self.view.frame.origin.y, self.view.frame.size.width-91*2, self.view.frame.size.height-220);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
Following code may help you.
You should get the correct cropFrame fist by below method
-(CGRect)cropRectForFrame:(CGRect)frame
{
// NSAssert(self.contentMode == UIViewContentModeScaleAspectFit, #"content mode must be aspect fit");
CGFloat widthScale = imageview.superview.bounds.size.width / imageview.image.size.width;
CGFloat heightScale = imageview.superview.bounds.size.height / imageview.image.size.height;
float x, y, w, h, offset;
if (widthScale<heightScale) {
offset = (imageview.superview.bounds.size.height - (imageview.image.size.height*widthScale))/2;
x = frame.origin.x / widthScale;
y = (frame.origin.y-offset) / widthScale;
w = frame.size.width / widthScale;
h = frame.size.height / widthScale;
} else {
offset = (imageview.superview.bounds.size.width - (imageview.image.size.width*heightScale))/2;
x = (frame.origin.x-offset) / heightScale;
y = frame.origin.y / heightScale;
w = frame.size.width / heightScale;
h = frame.size.height / heightScale;
}
return CGRectMake(x, y, w, h);
}
Then you need to call this method to get cropped image
- (UIImage *)imageByCropping:(UIImage *)image toRect:(CGRect)rect
{
// you need to update scaling factor value if deice is not retina display
UIGraphicsBeginImageContextWithOptions(rect.size,
/* your view opaque */ NO,
/* scaling factor */ 2.0);
// stick to methods on UIImage so that orientation etc. are automatically
// dealt with for us
[image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
- (UIImage*)imageByCropping:(CGRect)rect
{
//create a context to do our clipping in
UIGraphicsBeginImageContext(rect.size);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
//create a rect with the size we want to crop the image to
//the X and Y here are zero so we start at the beginning of our
//newly created context
CGRect clippedRect = CGRectMake(0, 0, rect.size.width, rect.size.height);
CGContextClipToRect( currentContext, clippedRect);
//create a rect equivalent to the full size of the image
//offset the rect by the X and Y we want to start the crop
//from in order to cut off anything before them
CGRect drawRect = CGRectMake(rect.origin.x * -1,
rect.origin.y * -1,
self.size.width,
self.size.height);
//draw the image to our clipped context using our offset rect
// CGContextDrawImage(currentContext, drawRect, self.CGImage);
[self drawInRect:drawRect]; // This will fix getting inverted image from context.
//pull the image from our cropped context
UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();
//pop the context to get back to the default
UIGraphicsEndImageContext();
//Note: this is autoreleased
return cropped;
}
Refer the below link for crop image
https://github.com/myang-git/iOS-Image-Crop-View
** How to Use **
Very easy! It is created to be a drop-in component, so no static library, no extra dependencies. Just copy ImageCropView.h and ImageCropView.m to your project, and implement ImageCropViewControllerDelegate protocol.
Use it like UIImagePicker:
- (void)cropImage:(UIImage *)image{
ImageCropViewController *controller = [[ImageCropViewController alloc] initWithImage:image];
controller.delegate = self;
[[self navigationController] pushViewController:controller animated:YES];
}
- (void)ImageCropViewController:(ImageCropViewController *)controller didFinishCroppingImage:(UIImage *)croppedImage{
image = croppedImage;
imageView.image = croppedImage;
[[self navigationController] popViewControllerAnimated:YES];
}
- (void)ImageCropViewControllerDidCancel:(ImageCropViewController *)controller{
imageView.image = image;
[[self navigationController] popViewControllerAnimated:YES];
}

UIView Greyscale , animation and performance issues

I'm developing an app and i'm implementing an application to manage orders some orders of a client.
In this view i have implemented a facebook style menu ( the new one that appears by shifting the whole window right) and i'have added a greyscale effect to the main view when it's shifted to right.
I've accomplished it by creating a UIImage of the current screen and by adding it over the real view and animating it's alpha from 1 to 0
Here's the code i've used to
-(void)toggleMenu {
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
if (![menu isOpened]){
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImage *blackAndWhiteImage = [UIImage getBlackAndWhiteVersionOfImage:viewImage];
overlayImage = [[UIImageView alloc]initWithImage:blackAndWhiteImage];
overlayImage.alpha = 0.1;
overlayImage.userInteractionEnabled=NO;
[self.view addSubview:overlayImage];
}
[UIView animateWithDuration:0.3 animations:^{
CGRect newRect = [self tabBarController].view.frame;
if (![menu isOpened]){
newRect.origin.x += 150;
[menu setOpened:YES];
overlayImage.alpha = 1.0;
} else {
newRect.origin.x -= 150;
[menu setOpened:NO];
overlayImage.alpha = 0;
}
[self tabBarController].view.frame = newRect;
} completion:^(BOOL finished){
if(![menu isOpened]){
[overlayImage removeFromSuperview];
overlayImage = nil;
}
}];
}
The problem is that i'm having issues with performances during animation ( little with iPhone 4 i'm trying an 3gs in next hours... )
Does anyone have any suggestions on what to do to get better performances ?
Regards
+ (UIImage *)getBlackAndWhiteVersionOfImage:(UIImage *)anImage {
UIImage *newImage;
if (anImage) {
CGColorSpaceRef colorSapce = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(nil, anImage.size.width * anImage.scale, anImage.size.height * anImage.scale, 8, anImage.size.width * anImage.scale, colorSapce, kCGImageAlphaNone);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetShouldAntialias(context, NO);
CGContextDrawImage(context, CGRectMake(0, 0, anImage.size.width, anImage.size.height), [anImage CGImage]);
CGImageRef bwImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSapce);
UIImage *resultImage = [UIImage imageWithCGImage:bwImage];
CGImageRelease(bwImage);
UIGraphicsBeginImageContextWithOptions(anImage.size, NO, anImage.scale);
[resultImage drawInRect:CGRectMake(0.0, 0.0, anImage.size.width, anImage.size.height)];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return newImage;
}
Making an image of the screen is costly. I'm not clear exactly what effect you want but I would overlay another view instead or animate a move of the main view instead.
For example, you can overlay a UIView that has a black background but an alpha of 0.1 to grey out a region.

Draw CGImage in UITableView

So, I have a custom cell and I need to draw all images as CGImage in tableView, but I can't get it working. I have created a test project and tested the code with simple views. Everything worked perfect, and when I copypasted the same code to my custom cell it stopped working. Here is the code:
-(void)drawRect:(CGRect)rect {
CGRect contentRect = self.contentView.bounds;
CGFloat boundsX = contentRect.origin.x;
UIImage *karmaImage = [UIImage imageNamed:#"karma.png"];
[self drawImage:karmaImage withRect:CGRectMake(boundsX + 255, 16, 14, 14)];
}
-(void)drawImage:(UIImage *)image withRect:(CGRect)rect {
CGImageRef imageRef = CGImageRetain(image.CGImage);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, rect, imageRef);
}
Any solutions?
Apple recommends to add your custom view to the UITableViewCell's contentView instead of changing UITableViewCell itself. See TimeZoneCell for an example.
In your drawRect method, you should probably call [super drawRect:rect];
OK, the problem was that contentView of the cell with custom background color was hiding the image. Here is the right code:
-(void) drawRect:(CGRect)rect{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:(232.0/255.0) green:(232.0/255.0) blue:(232.0/255.0) alpha:1.0].CGColor);
CGContextFillRect(context, rect);
UIImage *karmaImg = [UIImage imageNamed:#"karma.png"];
[self drawImage:karmaImg withContext:context atPoint:CGPointMake(boundsX + 255, 16)];
}
-(void)drawImage:(UIImage *)image withContext:(CGContextRef)context atPoint:(CGPoint)point {
if(image) {
CGContextDrawImage(context, CGRectMake(point.x, point.y, image.size.width, image.size.height), image.CGImage);
} else {
NSLog(#"Error: Image failed to load.");
}
}