does anybody know what to do with this problem: my image in a custom MKPolygonView is flipped upside down?
The idea (this is working OK already) is having an class "SnowmapsOverlayView" that extends "MKPolygonView", that displays a image. This image has a default location & size on the map (acts as a GroundOverlay in the Google Maps web API). This is all working fine, but the image is displayed upside down. I've tryed the following options, but with no result:
CGContextDrawImage draws image upside down when passed UIImage.CGImage
Thanks for any help or suggestions!
My code:
#import <MapKit/MapKit.h>
#interface SnowmapsOverlayView : MKPolygonView
{
}
#end
-----------------
#import "SnowmapsOverlayView.h"
#implementation SnowmapsOverlayView
-(id)initWithPolygon:(MKPolygon *)polygon
{
self = [super initWithPolygon:polygon];
return self;
}
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
UIImage *image = [UIImage imageNamed:#"snowmap.png"];
//This should do the trick, but is doesn't.. maybe a problem with the "context"?
//CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
//CGContextTranslateCTM(context, 0, image.size.height);
//CGContextScaleCTM(context, 1.0, -1.0);
CGRect overallCGRect = [self rectForMapRect:self.overlay.boundingMapRect];
CGContextDrawImage(context, overallCGRect, image.CGImage);
}
-(BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale
{
return YES;
}
Fixed! This is the code that gets the job done:
-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
UIImage *image = [UIImage imageNamed:#"snowmap.png"];
MKMapRect theMapRect = [self.overlay boundingMapRect];
CGRect theRect = [self rectForMapRect:theMapRect];
UIGraphicsPushContext(context);
[image drawInRect:theRect blendMode:kCGBlendModeNormal alpha:1.0];
UIGraphicsPopContext();
}
Have you tried subclassing MKOverlayView instead of MKPolygonView? The polygon class may do something weird to its coordinate system.
Related
I am working on App in which i need to give an cropping option. Once i select the image from camera or gallery it should open on editing page where we have Oval image with zooming & moving option. Once we click on apply the captured image should cropped in oval shape.
Now following screen is from aviary sdk. But it has square cropping & in need the cropping in oval shape. I tried to customise it but not able to do so.
Can anyone suggest me the easiest or the best suitable way to implement this.
Thanks.
- (UIImage *)croppedPhoto {
// For dealing with Retina displays as well as non-Retina, we need to check
// the scale factor, if it is available. Note that we use the size of teh cropping Rect
// passed in, and not the size of the view we are taking a screenshot of.
CGRect croppingRect = CGRectMake(imgMaskImage.frame.origin.x,
imgMaskImage.frame.origin.y, imgMaskImage.frame.size.width,
imgMaskImage.frame.size.height);
imgMaskImage.hidden=YES;
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(croppingRect.size, YES, [UIScreen mainScreen].scale);
} else {
UIGraphicsBeginImageContext(croppingRect.size);
}
// Create a graphics context and translate it the view we want to crop so
// that even in grabbing (0,0), that origin point now represents the actual
// cropping origin desired:
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(ctx, -croppingRect.origin.x, -croppingRect.origin.y);
[self.view.layer renderInContext:ctx];
// Retrieve a UIImage from the current image context:
UIImage *snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Return the image in a UIImageView:
return snapshotImage;
}
This type of masking you can perform or else you use the coco controls
.h file:-
#interface yourClass : UIImageView
#end
.m file:-
#import <QuartzCore/QuartzCore.h>
#implementation yourClass
- (void)awakeFromNib
{
[super awakeFromNib];
CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:#"ovalMask.png"] CGImage];
CGSize size = self.frame.size;
mask.frame = CGRectMake(0, 0, size.width, size.height);
self.layer.mask = mask;
[self.layer setMasksToBounds:YES];
}
#end
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.
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.");
}
}
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;
}
I've tried the following:
#implementation UIToolbar (Image)
-(void)drawRect:(CGRect)rect {
UIImage *img = [UIImage imageNamed: #"navigationBar.png"];
[img drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
Without any success, but it works perfectly with the exact same code for UINavigationBar+Image.
the drawRect method is not getting called for UIToolbar, why is that?
I found a solution
For UIToolBar, if you’re using it within a UINavigationController, you’ll want to also override drawLayer:inContext:, as this appears to be used instead of drawRect: when used within a navigation controller, for some weird reason.
Source: http://atastypixel.com/blog/making-uitoolbar-and-uinavigationbars-background-totally-transparent/
So I implemented - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx instead:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CALayer *imgLayer = [[CALayer alloc] init];
[imgLayer setContents:(id)[[UIImage imageNamed: #"toolbar.png"] CGImage]];
[imgLayer setBounds:CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height)];
[imgLayer setPosition:CGPointMake(self.bounds.size.width/2,self.bounds.size.height/2)];
[layer insertSublayer:imgLayer atIndex:0];
}