MapTypeStyle in MapKit - ios

I wonder to know if there is any way to configure our MapKit maps like we do with the MapTypeStyle object in the Google Maps API.
If I refer to Apple doc's, MKMapView has a mapType option that takes MKMapType constant but no styles parameters like MapOptions with the MapTypeStyle and the MapTypeStyler wich is very powerfull for fast maps customizing.
So my question is : Is there any way to achieve something similar with the MapKit framework, if not, what is the best framework/library to do this ? I'm thinking of MapBox and similar products.

There are a few options for you my friend. You could use one of these frameworks
http://cloudmade.com/products/iphone-sdk
https://github.com/route-me/route-me
Or you could just use mapbox. Their api looks pretty good.
Alternatively you supply you own map tiles and overlay mapkit. Something like this in a MKOverlayView
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
NSURL* fileURL = [(HeatMap*)self.overlay localUrlForStyle:#"alien" withMapRect:mapRect andZoomScale:zoomScale];
NSData *imageData = [NSData dataWithContentsOfURL:fileURL ];
if (imageData != nil) {
UIImage* img = [UIImage imageNamed:#"aTileX.png"];
// Perform the image render on the current UI context
UIGraphicsPushContext(context);
[img drawInRect:[self rectForMapRect:mapRect] blendMode:kCGBlendModeNormal alpha:1.0];
UIGraphicsPopContext();
}
}
Also check this out if you want unsupported "terrain" mode
http://openradar.appspot.com/9621632
I'm actually in the middle of a program that requires overlaying tiles over a map. This example has been very helpful. You'll want to look into MKOverlay and MKOverlayView. The project that I am doing involves using gheat. I am accessing the tiles through an NSURLConnection and storing them locally. A gist of my implementation.

There is no way to customize the map styles natively with mapkit. Your only option for this is to opt for a hybrid app approach, and then customize the styles using html/javascript in the page itself.

As drawing the tiles takes place in a private class called MKMapTileView you can not simply write a category. You have to implement another class for the custom drawing. The Methods of this class will be used to overload the implementation of MKMapTileView during runtime:
Header file:
#interface MyColorMap : NSObject
+ (void)overLoadMethods:(Class)destinationClass;
#end
Imlementation:
#import "MyColorMap.h"
#import <objc/runtime.h>
#implementation MyColorMap
+ (void)overLoadMethods:(Class)destinationClass {
// get the original method for drawing a tile
Method originalDrawLayer = class_getInstanceMethod(destinationClass, #selector(drawLayer:inContext:));
// get the method we will replace with the original implementation of 'drawLayer:inContext:' later
Method backupDrawLayer = class_getInstanceMethod([self class], #selector(backupDrawLayer:inContext:));
// get the method we will use to draw our own colors
Method myDrawLayer = class_getInstanceMethod([self class], #selector(myDrawLayer:inContext:));
// dito with the implementations
IMP impOld = method_getImplementation(originalDrawLayer);
IMP impNew = method_getImplementation(myDrawLayer);
// replace the original 'drawLayer:inContext:' with our own implementation
method_setImplementation(originalDrawLayer, impNew);
// set the original 'drawLayer:inContext:' implementation to our stub-method, so wie can call it later on
SEL selector = method_getName(backupDrawLayer);
const char *types = method_getTypeEncoding(backupDrawLayer);
class_addMethod(destinationClass, selector, impOld, types);
}
- (void)backupDrawLayer:(CALayer*)l inContext:(CGContextRef)c {
// stub method, implementation will never be called. The only reason we implement this is so we can call the original method durring runtime
}
- (void)myDrawLayer:(CALayer*)l inContext:(CGContextRef)c {
// set background to white so wie can use it for blendmode
CGContextSetFillColorWithColor(c, [[UIColor whiteColor] CGColor]);
CGContextFillRect(c, CGContextGetClipBoundingBox(c));
// set blendmode so the map will show as grayscale
CGContextSetBlendMode(c, kCGBlendModeLuminosity);
// kCGBlendModeExclusion for inverted colors etc.
// calling the stub-method which will become the original method durring runtime
[self backupDrawLayer:l inContext:c];
// if you want more advanced manipulations you can alter the context after drawing:
// int w = CGBitmapContextGetWidth(c);
// int h = CGBitmapContextGetHeight(c);
//
// unsigned char* data = CGBitmapContextGetData(c);
// if (data != NULL) {
// int maxY = h;
// for(int y = 0; y<maxY; y++) {
// for(int x = 0; x<w; x++) {
//
// int offset = 4*((w*y)+x);
// char r = data[offset];
// char g = data[offset+1];
// char b = data[offset+2];
// char a = data[offset+3];
//
// // do what ever you want with the pixels
//
// data[offset] = r;
// data[offset+1] = g;
// data[offset+2] = b;
// data[offset+3] = a;
// }
// }
// }
}
now you have to call [MyColorMap overLoadMethods:NSClassFromString(#"MKMapTileView")] at some point before using a MKMapView

Related

CGPatternDrawPatternCallback not called in iOS 12 /Objective C

The following Objective-C code has worked correctly in iOS 9 - 11. It draws a checkerboard with colored squares. For some reason the callback that adds the colors is not being called in iOS 12 and Xcode 10.0. I've tried a variety of fixes but nothing obvious has worked. Something seems to have changed in iOS 12 but nothing I tried has fixed the problem.
-(id)initWithFrame:(CGRect)frame checkerSize:(CGSize)checkerSize darkColor:(UIColor *)darkShade lightColor:(UIColor *)lightShade {
self = [super initWithFrame:frame];
if(self != nil)
{
// Initialize the property values
checkerHeight = checkerSize.height;
checkerWidth = checkerSize.width;
self.darkColor = darkShade;
self.lightColor = lightShade;
// Colored Pattern setup
CGPatternCallbacks coloredPatternCallbacks = {0, ColoredPatternCallback, NULL};
CGRect clippingRectangle = CGRectMake(0.0, 0.0, 2.0*checkerWidth, 2.0*checkerHeight);
// First we need to create a CGPatternRef that specifies the qualities of our pattern.
CGPatternRef coloredPattern = CGPatternCreate(
(__bridge_retained void *)self, // 'info' pointer for our callback
clippingRectangle, // the pattern coordinate space, drawing is clipped to this rectangle
CGAffineTransformIdentity, // a transform on the pattern coordinate space used before it is drawn.
2.0*checkerWidth, 2.0*checkerHeight, // the spacing (horizontal, vertical) of the pattern - how far to move after drawing each cell
kCGPatternTilingNoDistortion,
true, // this is a colored pattern, which means that you only specify an alpha value when drawing it
&coloredPatternCallbacks); // the callbacks for this pattern.
// To draw a pattern, you need a pattern colorspace.
// Since this is an colored pattern, the parent colorspace is NULL, indicating that it only has an alpha value.
CGColorSpaceRef coloredPatternColorSpace = CGColorSpaceCreatePattern(NULL);
CGFloat alpha = 1.0;
// Since this pattern is colored, we'll create a CGColorRef for it to make drawing it easier and more efficient.
// From here on, the colored pattern is referenced entirely via the associated CGColorRef rather than the
// originally created CGPatternRef.
coloredPatternColor = CGColorCreateWithPattern(coloredPatternColorSpace, coloredPattern, &alpha);
CGColorSpaceRelease(coloredPatternColorSpace);
CGPatternRelease(coloredPattern);
}
return self;
}
void ColoredPatternCallback(void *info, CGContextRef context) {
HS_QuartzPatternView *self = (__bridge_transfer id)info; // needed to access the Obj-C properties from the C function
CGFloat checkerHeight = [self checkerHeight];
CGFloat checkerWidth = [self checkerWidth];
// "Dark" Color
UIColor *dark = [self darkColor];
CGContextSetFillColorWithColor(context, dark.CGColor);
CGContextFillRect(context, CGRectMake(0.0, 0.0, checkerWidth, checkerHeight));
CGContextFillRect(context, CGRectMake(checkerWidth, checkerHeight, checkerWidth, checkerHeight));
// "Light" Color
UIColor *light = [self lightColor];
CGContextSetFillColorWithColor(context, light.CGColor);
CGContextFillRect(context, CGRectMake(checkerWidth, 0.0, checkerWidth, checkerHeight));
CGContextFillRect(context, CGRectMake(0.0, checkerHeight, checkerWidth, checkerHeight));
}

Bitmap causing memory leak on device

i am using simple bitmap technique to convert text into image , after that i divide this image into raster and later on i am calculating the percentage of black pixels in each raster rectangle. Every thing works fine on simulator but get crashed on Device.here is some related code
-(int)blackValue:(UIImage *)image rect:(CGRect)rect {
int pixelInRect = (int)rect.size.width * rect.size.height;
__block int blackCount = 0;
ImageBitmap * imageBitmap = [[ImageBitmap alloc] initWithImage:image bitmapInfo:(CGBitmapInfo)kCGImageAlphaNoneSkipLast];
for (int x = 0; x <(int) rect.size.width; x++) {
for (int y = 0; y < (int) rect.size.height; y++) {
Byte * pixel = [imageBitmap pixelAtX:(int)rect.origin.x Y:(int)rect.origin.y];
Byte red = pixel[0];
if (red < 0.1)
{
blackCount++;
}
}
}
return blackCount/pixelInRect;
}
- (NSDictionary *)rasterizeBitmap:(UIImage *)image size:(CGFloat)size {
CGFloat width =(int) (image.size.width/size);
CGFloat height =(int) (image.size.height/size);
NSMutableArray *fields = [NSMutableArray array];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
CGRect rect = CGRectMake(x * size, y * size, size, size);
CGPoint center = CGPointMake(x * size + size/2.0, image.size.height - (y * size + size/2.0));
double black = [self blackValue:image rect:rect];
Field * field = [[Field alloc] init];
field.center = center;
field.black = black;
[fields addObject:field];
}
}
return #{#"width":#(width) , #"fields":fields};
}
when i have try to run it in Profile i got the below result
Can some one suggestion me how can i over come the memory issue?
The problem is that you're manually allocating memory in your ImageBitmap object, but you are never releasing it.
The two suspects are the bitmap context (context), and the bitmap data (contextData). Both of these are not managed by ARC, so you'll want to be freeing both of these yourself once you are done with them.
In ARC, you can simply implement the dealloc method in your ImageBitmap class and put your cleanup code there.
For example:
-(void) dealloc {
CGContextRelease(context); // releases the bitmap context, if it was created (CGContextRelease checks for NULL)
free(contextData); // releases the bitmap data (it was explicitly created, so no need to check)
}
It's also worth noting you should make init unavailable, and mark your designated initialiser.
This is because you cannot use your imageFromContext and pixelAtX:Y: instance methods without having created your instance through your custom initWithSize:bitmapInfo: initialiser, as it creates the bitmap context and allocates the memory for the bitmap data.
Therefore if you were to create your instance by calling init, and call one of your instance methods, you would most likely get a crash.
To remedy this, you can mark the init method as unavailable in your ImageBitmap.h file, and also mark your initWithSize:bitmapInfo: method as the designated initialiser.
-(instancetype) init NS_UNAVAILABLE;
-(id)initWithSize:(CGSize)size bitmapInfo:(CGBitmapInfo)bmInfo NS_DESIGNATED_INITIALIZER;
All the NS_UNAVAILABLE does is prevent you from creating your instance by just calling init, forcing you to use your custom initialisers.
If you try to do [[ImageBitmap alloc] init], the compiler will show you the following error:
All the NS_DESIGNATED_INITIALIZER does is make sure that any extra initialisers in ImageBitmap must create new instances through your initialiser, and will show you the following warning if they don't:
See here for more info on NS_DESIGNATED_INITIALIZER.
Now, in practise these are really just formalities as you're the only one who's going to be using this, and you know you have to use the custom initialisers. However, it's good to get these formalities right if you ever want to share your code with other people.

Fill UINavigationBar background with CGGradientRef

I would like to fill my UINavigationBar background with CGGradientRef instead of a picture file (e.g. myBackground.png). This practice will avoid having to create a PNG file for each screen size and also save storage space.
I've seen that it's possible to create an UIImage drawing a gradient from scratch and using:
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
Also, I've seen that I can assign an UIImage to a UINavigationBar using:
myNavigationController.navigationBar.barTintColor = [UIColor colorWithPatternImage:image]];
However, I have not been able to put these two together. Some help will be appreciated. Thank you.
Here is a overloaded UINavigationBar class using a two point gradient, although it could easily be improved to cover multi-point gradients.
To enable this bar style, select the navigation bar object in the navigation scene of the storyboard, and set its custom class to GradientNavigationBar.
In this case the awakeFromNib call is used to change the background, (assuming that the Navigation bar class has been changed in the storyboard), in the case that the Navigation bar is instantiated programatically, the customization call should be made in the appropriate position in the code.
The solution works by converting the colors passed to it to an array of CGFloat, then generating a CGGradientRef object, using those colors, creating an image and then using the setBackgroundImage:forBarMetrics call to set the background as required.
#interface GradientNavigationBar
#end
#implementation GradientNavigationBar
-(void) awakeFromNib {
[self setGradientBackground:[UIColor redColor]
endColor:[UIColor yellowColor]];
}
-(void) setGradientBackground:(UIColor *) startColor endColor:(UIColor *) endColor {
// Convert the colors into a format where they can be used with
// core graphics
CGFloat rs, gs, bs, as, re, ge, be, ae;
[startColor getRed:&rs green:&gs blue:&bs alpha:&as];
[endColor getRed:&re green:&ge blue:&be alpha:&ae];
CGFloat colors [] = {
rs, gs, bs, as,
re, ge, be, ae
};
// Generate an Image context with the appropriate options, it may
// be enhanced to take into account that Navbar heights differ
// eg between landscape and portrait in the iPhone
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
CGContextRef gc = UIGraphicsGetCurrentContext();
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
// The gradient element indicates the colors to be used and
// the color space
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2);
CGColorSpaceRelease(baseSpace), baseSpace = NULL;
// Draw the gradient
CGContextDrawLinearGradient(gc, gradient, CGPointMake(0, 0),CGPointMake(0, self.bounds.size.height),0);
// Capture the image
UIImage * backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
// The critical call for setting the background image
// Note that separate calls can be made e.g. for the compact
// bar metric.
[self setBackgroundImage:backgroundImage forBarMetrics:UIBarMetricsDefault];
CGGradientRelease(gradient), gradient = NULL;
UIGraphicsEndImageContext();
}
#end

How do I make a part of a UILabel visually be a block quote?

How do I make a specific portion of a UILabel look like a blockquote, or have there be a vertical line on the left side of the text? Would TextKit come in here? If so, how?
Mail.app does this (see the colored portions and the line on the side of them):
How would I replicate this effect without using multiple UILabels (which as I'm creating it dynamically would be rather gross)?
Create a view (XIB) with this general layout like the picture above. There is a UILabel, a UITextView and a UIView (the blue rectangle is a UIView with the background color set). Let's call it ThreadView.xib. Hook up the label, textview and view as properties to the view.
We can then have a method to generate one of these views for us to use and a method to add more ThreadViews as subviews based on how many comments/replies a post has.
+ (instancetype)threadViewWithLabelText:(NSString *)labelText
textViewText:(NSString *)textViewText
color:(UIColor *)color
{
ThreadView *threadView = [[[NSBundle mainBundle] loadNibNamed:#"ThreadView"
owner:self
options:nil] firstObject];
if (threadView) {
threadView.label.text = labelText;
threadView.textView.text = textViewText;
threadView.colorView.backgroundColor = color;
}
return threadView;
}
- (void)addCommentView:(ThreadView *)threadView
toViewController:(UIViewController *)viewController
{
threadView.frame = CGRectMake(self.frame.origin.x + 25,
self.textView.frame.origin.y + self.textView.frame.size.height,
self.frame.size.width - (self.frame.origin.x + 10),
self.frame.size.height - (self.textView.frame.origin.y + self.textView.frame.size.height));
[viewController.view addSubview:threadView];
}
Now, in the main view controller, we can create and add these views with just these two method calls:
- (void)viewDidLoad
{
[super viewDidLoad];
// Load the first post
ThreadView *originalPost = [ThreadView threadViewWithLabelText:#"10 Some Words 2014 More Words"
textViewText:loremIpsum
color:[UIColor blueColor]];
originalPost.frame = CGRectMake(self.view.frame.origin.x + 8,
self.view.frame.origin.y + 15,
self.view.frame.size.width - 8,
self.view.frame.size.height - 15);
[self.view addSubview:originalPost];
// Load a comment post
ThreadView *commentPost = [ThreadView threadViewWithLabelText:#"12 December 2014 Maybe A Username"
textViewText:loremIpsum
color:[UIColor greenColor]];
[originalPost addCommentView:commentPost
toViewController:self];
}
This will give us a result like in the picture below. This code could use some refactoring/restructuring, but this should get you started. You can also mix up use of autolayout and/or setting the frames of the views.
Try this?
NSString *html =[NSString stringWithFormat:
#"<html>"
" <head>"
" <style type='text/css'>"
"ul"
"{"
" list-style-type: none;"
"}"
" </style>"
" </head>"
" <body>"
"%# - PARENT"
"<ul>"
"<li>"
"%# - CHILD 1"
"</li>"
"<li>"
"%# - CHILD 2 "
"</li>"
"</ul>"
"</body>"
"</html>"
,#"Parent Title", #"Child Description 1", #"Child Description 2"];
NSError *err = nil;
_label.attributedText =
[[NSAttributedString alloc]
initWithData: [html dataUsingEncoding:NSUTF8StringEncoding]
options: #{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
documentAttributes: nil
error: &err];
if(err)
NSLog(#"Unable to parse label text: %#", err);
And the Result is like this .
This can be easily done with Text Kit. I do stuff like this in my app. The difference is I use boxes (nested if needed) to mark each text block. Here is what you should do:
Parse html string (or whatever you use to mark text), mark each text block quote with a custom attribute, like MyTextBlockAttribute, save ranges of each text block (i.e. block quote) and add it as a attribute to the related range of the attributed string(construct this attributed string from your content) and the list attached to the content. Lets call this list MyTextBlockList.
draw text with Text Kit yourself. draw background first (white color, light gray color.. etc, whatever), draw text or vertical lines next. Since you can get each text block's range by loop through the list, you can get bounding rect of these blocks with method [NSLayoutManager range: inTextContainer:textContainer].
Here is the code I used in my app:
// subclass of NSTextContainer
#import "MyTextContainer.h"
#import "MyBlockAttribute.h"
#interface MyTextContainer ()
#property (nonatomic) BOOL isBlock;
#end
#implementation MyTextContainer
- (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect
atIndex:(NSUInteger)characterIndex
writingDirection:(NSWritingDirection)baseWritingDirection
remainingRect:(CGRect *)remainingRect {
CGRect output = [super lineFragmentRectForProposedRect:proposedRect
atIndex:characterIndex
writingDirection:baseWritingDirection
remainingRect:remainingRect];
NSUInteger length = self.layoutManager.textStorage.length;
MyTextBlockAttribute *blockAttribute;
if (characterIndex < length) {
blockAttribute = [self.layoutManager.textStorage attribute:MyTextBlockAttributeName atIndex:characterIndex effectiveRange:NULL]; // MyTextBlockAttributeName is a global NSString constant
}
if (blockAttribute) { // text block detected, enter "block" layout mode!
output = CGRectInset(output, blockAttribute.padding, 0.0f); // set the padding when constructing the attributed string from raw html string, use padding to control nesting, inner boxes have bigger padding, again, this is done in parsing pass
if (!self.isBlock) {
self.isBlock = YES;
output = CGRectOffset(output, 0.0f, blockAttribute.padding);
}
} else if (self.isBlock) {
self.isBlock = NO; // just finished a block, return back to the "normal" layout mode
}
// no text block detected, not just finished a block either, do nothing, just return super implementation's output
return output;
}
#end
// drawing code, with drawRect: or other drawing technique, like drawing into bitmap context, doesn't matter
- (void)drawBlockList:(NSArray *)blockList content:(MyContent *)content {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5f);
[[UIColor colorWithWhite:0.98f alpha:1.0f] setFill];
CGContextSaveGState(context);
MyTextContainer *textContainer = content.textContainer;
// since I draw boxes, I have to draw inner text block first, so use reverse enumerator
for (MyTextBlockAttribute *blockAttribute in [blockList reverseObjectEnumerator]) {
if (blockAttribute.noBackground) { // sometimes I don't draw boxes in some conditions
continue;
}
CGRect frame = CGRectIntegral([content.layoutManager boundingRectForGlyphRange:blockAttribute.range inTextContainer:textContainer]);
frame.size.width = textContainer.size.width - 2 * (blockAttribute.padding - MyDefaultMargin); // yeah... there is some margin around the boxes, like html's box model, just some simple math to calculate the accurate rectangles of text blocks
frame.origin.x = blockAttribute.padding - MyDefaultMargin;
frame = CGRectInset(frame, 0, -MyDefaultMargin);
if (blockAttribute.backgroundColor) { // some text blocks may have specific background color
CGContextSaveGState(context);
[blockAttribute.backgroundColor setFill];
CGContextFillRect(context, frame);
CGContextRestoreGState(context);
} else {
CGContextFillRect(context, frame);
}
CGContextStrokeRect(context, frame); // draw borders of text blocks in the last
}
CGContextRestoreGState(context);
}
- (UIImage *)drawContent:(MyContent *)content {
UIImage *output;
UIGraphicsBeginImageContextWithOptions(content.bounds.size, YES, 0.0f); // bounds is calculated in other places
[[UIColor whiteColor] setFill];
UIBezierPath *path = [UIBezierPath bezierPathWithRect:content.bounds];
[path fill];
[self drawBlockList:content.blockList content:content]; // draw background first!
[content.layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, content.textStorage.length) atPoint:CGPointZero]; // every content object has a set of Text Kit core objects, textStorage, textContainer, layoutManager
output = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return output;
}
In your case, you don't draw boxes, you draw left borders instead. The technique is the same, hope this can help you!
If you are targeting iOS lower than 7, You can do somethingsimilar by using Core Text, but since Core Text is kind of old C opaque types implementation, I suggest you to use DTCoreText.
If you are using >=iOS7 you can use NSAttributed string and NSXMLDocument. Even if attributed string are available from 3.x they only added them into UIKIT objects into ios6 and changed radically the UIKit behavior in managing them into iOS7.
NSXMLDocument it's helpful because you can render your string representing them as HTML.
This may sound counterintuitive, but have you considered popping it all in a tableView ? you can exploit the indentLevelAtIndexPath: stuff....

unsigned char allocation comes nils in offset value objective c

I am getting the pixel colour values from touch points. I am successfully doing this but after sometimes app is giving the error( EXC_BAD_ACCESS(CODE=1,address=0x41f6864). Its memory allocation problem here is the source code for your reference.
- (UIColor *) getPixelColorAtLocation:(CGPoint)point {
UIColor* color = nil;
#try{
{
CGImageRef inImage = drawImage.image.CGImage;
// Create off screen bitmap context to draw the image into. Format ARGB is 4 bytes for each pixel: Alpa, Red, Green, Blue
CGContextRef cgctx = [self createARGBBitmapContextFromImage:inImage];
if (cgctx == NULL)
{
return nil; /* error */
}
size_t w = CGImageGetWidth(inImage);
size_t h = CGImageGetHeight(inImage);
CGRect rect = {{0,0},{w,h}};
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage (cgctx, rect, inImage);
// Now we can get a pointer to the image data associated with the bitmap
// context.
unsigned char *data = {0};
data=(unsigned char*) calloc(CGImageGetHeight(inImage) * CGImageGetWidth(inImage) , CGBitmapContextGetHeight(cgctx)*CGBitmapContextGetWidth(cgctx));
data= CGBitmapContextGetData (cgctx);
if( data !=NULL ) {
//offset locates the pixel in the data from x,y.
//4 for 4 bytes of data per pixel, w is width of one row of data.
int offset = 4*((w*round(point.y))+round(point.x));
// NSLog(#"%s111111",data);
int alpha = data[offset]; /////// EXC_BAD_ACCESS(CODE=1,address=0x41f6864)
int red = data[offset+1];
int green = data[offset+2];
int blue = data[offset+3];
//NSLog(#"offset: %i colors: RGB A %i %i %i %i",offset,red,green,blue,alpha);
color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)];
}
// When finished, release the context
//CGImageRelease(*data);
CGContextRelease(cgctx);
// Free image data memory for the context
if (data)
{
free(data);
}
}
#catch (NSException *exception) {
}
return color;
}
The memory management in your code appears to be wrong:
Declare data and pointlessly assign a value to it:
unsigned char *data = {0};
Allocate a memory block and store a reference to it in data - overwriting the pointless initialisation:
data = (unsigned char *)calloc(CGImageGetHeight(inImage) * CGImageGetWidth(inImage), CGBitmapContextGetHeight(cgctx) * CGBitmapContextGetWidth(cgctx));
Now get a reference to a different memory block and store it in data, throwing away the reference to the calloc'ed block:
data = CGBitmapContextGetData (cgctx);
Do some other stuff and then free the block you did not calloc:
free(data);
If you are allocating your own memory buffer you should pass it to CGBitmapContextCreate, however provided you are using iOS 4+ there is no need to allocate your own buffer.
As to the memory access error, you are doing no checks on the value of point and your calculation would appear to be producing a value of offset which is incorrect. Add checks on the values of point and offset and take appropriate action if they are out of bounds (you will have to decide what that should be).
HTH
The problem may cause by the point is out of image rect,so you can use
try{
int offset = 4*((w*round(point.y))+round(point.x));
int alpha = data[offset];
int red = data[offset+1];
int green = data[offset+2];
int blue = data[offset+3];
color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f)
alpha:(alpha/255.0f)];
}catch(NSException e){
}
to avoid the EXC_BAD_ACCESS

Resources