CGContextSetRGBFillColor too few arguments - ios

I am trying to set a color to my CGContextSetRGBFillColor in this way:
- (void) drawArrowWithContext:(CGContextRef)context atPoint:(CGPoint)startPoint withSize: (CGSize)size lineWidth:(float)width arrowHeight:(float)aheight andColor:(UIColor *)color
{
CGContextSetRGBFillColor (context,color,color,color,1);
CGContextSetRGBStrokeColor (context, color.CGColor);
....
}
...but I am getting in both cases the error "Too few arguments, should be 5, are 2". How can I fix this issue?

Seeing your other question, I would suggest that you stop for an hour and do some reading of the docs rather than simply trying to hammer your way through without understanding or learning anything.
You have a problem in your code: you are passing in a UIColor and trying to use it in a function which takes floats as arguments. Either change the params for you method or use a different CoreGraphics function which can accept a UIColor (or rather the CGColor represenation of that).
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextSetStrokeColorWithColor(context,[color CGColor]);

From the documentation:
void CGContextSetRGBFillColor (
CGContextRef c,
CGFloat red,
CGFloat green,
CGFloat blue,
CGFloat alpha
);
All you need to do is break apart your UIColor using
- (BOOL)getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha

Related

Algorithm to detect GPS environment you're in

i need to detect in which environment the app user is in. For example is he in a forrest, or in a city, or in the near of a sea...
Therefore i just made a image from the map, calculated the average pixel and compare this color to a green, blue, brown, gray color...
But this is very inaccurate since there could be a sea in the near but the average color isn't blueish at all. Also the comparison of colors does not always match the expectations you have.
Is there any better way to detect the environment the user is in ? Since it has to work worldwide i do not think there is any possible service which can give me reliable information about forrest, seas, maybe mountains...
Maybe someone of you has an idea how to solve this or has a hint for me.
Here you can see how i tried this until this point (its objective-c code, but i am glad for any answer, also answers that do not have anything to do with iOS app developing).
- (UIColor *)mergedColor
{
CGSize size = {1, 1};
UIGraphicsBeginImageContext(size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(ctx, kCGInterpolationMedium);
[self drawInRect:(CGRect){.size = size} blendMode:kCGBlendModeCopy alpha:1];
uint8_t *data = CGBitmapContextGetData(ctx);
UIColor *color = [UIColor colorWithRed:data[2] / 255.0f
green:data[1] / 255.0f
blue:data[0] / 255.0f
alpha:1];
UIGraphicsEndImageContext();
return color;
}
UIColor *forrest = [UIColor greenColor];
const CGFloat *components1 = CGColorGetComponents([[self.view mergedColor] CGColor]);
const CGFloat *components2 = CGColorGetComponents([forrest CGColor]);
double fDistance = sqrt(pow(components1[0] - components2[0], 2) + pow(components1[1] - components2[1], 2) + pow(components1[2] - components2[2], 2));
double fPercentage = fDistance / sqrt(pow(255, 2) + pow(255, 2) + pow(255, 2));
This idea for a solution does not involve image processing.
But if you know your latitude and longitude (and this is easy to get from CoreLocation), you can pass it to a Geocoding service.
For example, when I look at Google's Geocoding API, I see a section for "Address Types and Address Component Types", and types include:
natural_feature indicates a prominent natural feature.
airport
indicates an airport.
park indicates a named park.
point_of_interest
indicates a named point of interest. Typically, these "POI"s are
prominent local entities that don't easily fit in another category
such as "Empire State Building" or "Statue of Liberty."
So there may be enough in that API for you to work with.

Assigning of floats into global array

I'm trying to customize grouped UITableViewCell's backgroundView with a gradient, based on code I find on this blog. It's a subclass of UIView for use on cell.backgroundView.
The colors of the background's gradient are defined like this on the original code :
#define TABLE_CELL_BACKGROUND { 1, 1, 1, 1, 0.866, 0.866, 0.866, 1} // #FFFFFF and #DDDDDD
And then, used like this on the drawRect of the subclassed backgroundView:
CGFloat components[8] = TABLE_CELL_BACKGROUND;
myGradient = CGGradientCreateWithColorComponents(myColorspace, components , locations, 2);
I'm trying to implement a function to set start and end color for the gradient, which takes two UIColors and then fill in a global float array float startAndEndColors[8] (in .h / #interface) for later use:
-(void)setColorsFrom:(UIColor*)start to:(UIColor*)end{
float red = 0.0, green = 0.0, blue = 0.0, alpha =0.0, red1 = 0.0, green1 = 0.0, blue1 = 0.0, alpha1 =0.0;
[start getRed:&red green:&green blue:&blue alpha:&alpha];
[end getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1];
//This line works fine, my array is successfully filled, just for test
float colorsTest[8] = {red, green, blue, alpha, red1, green1, blue1, alpha1};
//But for this one, I just have an error.
//"Expected expression"
// \
// v
startAndEndColors = {red, green, blue, alpha, red1, green1, blue1, alpha1};
}
But it throw me this error "Expected expression" at assignation.
I tried with CGFloat, desperately adding random const, but I quickly ran out of ideas.
I simply don't get it, why can't I fill my float array this way? What am I doing wrong?
Comment added as answer:
The only way of creating an array that way is dynamically in code. If you are adding to an iVar (class variable) you need to go through one by one because the memory has already been allocated at initialization. So use startAndEndColors[0] = ..., etc.
As for your follow up question: No, there is no way to assign values in that way to memory that has already been initialized in the allocation phase. If you used std::vector or other objects then it would be possible.
A way around that would be something like this in your header
CGFloat *startAndEndColors;
And then something like this in your implementation
float colorsTest[8] = {red, green, blue, alpha, red1, green1, blue1, alpha1};
startAndEndColors = colorsTest;
That way you can initialize it the way you want to, but you have no guarantee of the number of objects in your startAndEndColors object. You could later assign it to something of the wrong size and cause crashes if you try to access outside of it's bounds.

How to input an argument into CGContextSetRGBFillColor?

I'm trying to input the arguments for CGContextSetRGBFillColor using a data type. For example:
NSString *colorcode = ctx, 0, 1, 0, 0;
CGContextSetRGBFillColor(colorcode);
But I get an error saying that I have too few arguments.
I want to change the arguments (ctx, 0, 1, 0, 1 ) sent to CGContextSetRGBFillColor depending on the users actions.
I want to input the argument for CGContextSetRGBFillColor using a data type because the values of it is set in a separate view controller. Or can I directly input the arguments to CGContextSetRGBFillColor and then bring it over to the other view controller to use it?
Try using a UIColor object to store the user's selected color. You can create one like this:
UIColor *color = [UIColor colorWithRed:0 green:1 blue:0 alpha:0];
Then when it's time to use it as the fill color, you can do this:
CGContextSetFillColorWithColor(ctx, color.CGColor);
I should mention that if you are not using ARC, you need to retain and release color appropriately.
Sounds like what you really need to be doing is:
CGContextSetRGBFillColor (ctx, 0.0f, 1.0f, 0.0f, 1.0f);
Where each color component is some fraction between 0.0 and 1.0.
Why are you using a NSString?
Here is the documentation on Apple's website.
I want to input the argument for CGContextSetRGBFillColor using a data type because the values of it is set in a separate view controller.
You may be interested in the CGColor class, or, on iOS specifically, UIColor.
Or can I directly input the arguments to CGContextSetRGBFillColor …
That's the only way to input the arguments to CGContextSetRGBFillColor.
… and then bring it over to the other view controller to use it?
That doesn't make sense. Bring what over?
If you want to bring the color from one view controller to another, that's best done by creating a color object—either a CGColor or a UIColor—and passing that.

Creating gradient and returning for a method

Sorry for noobish question about iPhone and Quartz programming. Just started my conversion from C++ to Objective-C :)
So, I have such a class method
+(CGGradientRef)CreateGradient:(UIColor*)startColor endColor:(UIColor*)endColor
{
CGGradientRef result;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[2] = {0.0f, 1.0f};
CGFloat startRed, startGreen, startBlue, startAlpha;
CGFloat endRed, endGreen, endBlue, endAlpha;
[endColor getRed:&endRed green:&endGreen blue:&endBlue alpha:&endAlpha];
[startColor getRed:&startRed green:&startGreen blue:&startBlue alpha:&startAlpha];
CGFloat componnents[8] = {
startRed, startGreen, startBlue, startAlpha,
endRed, endGreen, endBlue, endAlpha
};
result = CGGradientCreateWithColorComponents(colorSpace, componnents, locations, 2);
CGColorSpaceRelease(colorSpace);
return result;
}
and its usage.
-(void)FillGradientRect:(CGRect)area startColor:(UIColor *)startColor endColor:(UIColor *)endColor isVertical:(BOOL)isVertical
{
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
CGGradientRef gradient = [Graphics CreateGradient:startColor endColor:endColor];
CGPoint startPoint, endPoint;
if (isVertical) {
startPoint = CGPointMake(CGRectGetMinX(area), area.origin.y);
endPoint = CGPointMake(startPoint.x, area.origin.y + area.size.height);
}else{
startPoint = CGPointMake(0, area.size.height / 2.0f);
endPoint = CGPointMake(area.size.width, startPoint.y);
}
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
UIGraphicsPopContext();
}
everything works as expected. But, when I run the Analyze tool from Xcode 4, I'm getting a warning about memory leak in method CreateGradient for result variable. Well, I understand what's that about, but in my calling method I'm releasing the gradient object (CGGradientRelease(gradient);).
So, who is wrong and how to make Analyze tool happy?
Thx
Since CGGradientRef is a Core Foundation type of object, you can autorelease it. Just add this line before returning the gradient:
[(id)result autorelease];
If the goal is solely to keep the analyzer happy in ARC, then just make it a C function rather than objective-C - i.e.:
CGGradientRef CreateGradient(UIColor *startColor, UIColor * endColor)
The Core Foundation naming scheme then applies which says that a function with Create in the name is treated as returning a retained object (and it is the caller's responsibility to release it). This satisfies the analyser.
If you want an autoreleased variable, then transfer ownership of the CG type to ARC:
id arc_result = (__bridge_transfer id)result
However, if you do that, you need to return the objective-c type (arc_result), not the CG-type. If you return the CG type, there will be no retained references to arc_result, and so the compiler will clean it up as you return from the function.
You could use this hack to effect a CG-type autorelease:
dispatch_async(dispatch_get_main_queue(), ^{
CGGradientRelease(result);
});
It would satisfy the analyser and probably work - though I would consider it to be pretty unsafe!

CGPathCreateCopyByStrokingPath equivalent on iOS4?

I found CGPathCreateCopyByStrokingPath on iOS 5.0 quite convenient to use but it is available on iOS 5 and later.
Is there any simple way to achieve the same path copying on iOS 4?
I use this, which is compatible across IOS5 and IOS4+. It works 100% if you use the same fill + stroke color. Apple's docs are a little shady about this - they say "it works if you fill it", they don't say "it goes a bit wrong if you stroke it" - but it seems to go slightly wrong in that case. YMMV.
// pathFrameRange: you have to provide something "at least big enough to
// hold the original path"
static inline CGPathRef CGPathCreateCopyByStrokingPathAllVersionsOfIOS( CGPathRef
incomingPathRef, CGSize pathFrameRange, const CGAffineTransform* transform,
CGFloat lineWidth, CGLineCap lineCap, CGLineJoin lineJoin, CGFloat miterLimit )
{
CGPathRef result;
if( CGPathCreateCopyByStrokingPath != NULL )
{
/**
REQUIRES IOS5!!!
*/
result = CGPathCreateCopyByStrokingPath( incomingPathRef, transform,
lineWidth, lineCap, lineJoin, miterLimit);
}
else
{
CGSize sizeOfContext = pathFrameRange;
UIGraphicsBeginImageContext( sizeOfContext );
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(c, lineWidth);
CGContextSetLineCap(c, lineCap);
CGContextSetLineJoin(c, lineJoin);
CGContextSetMiterLimit(c, miterLimit);
CGContextAddPath(c, incomingPathRef);
CGContextSetLineWidth(c, lineWidth);
CGContextReplacePathWithStrokedPath(c);
result = CGContextCopyPath(c);
UIGraphicsEndImageContext();
}
}
Hmmm -- don't know if this qualifies as "simple", but check out Ed's method in this SO post.

Resources