Comparing equality of [UIColor colorWithPatternImage:] - ios

I want to compare two UIColors that I generated using [UIColor colorWithPatternImage:] for equality. I can't seem to figure out how to do this.
[[UIColor colorWithPatternImage:[UIImage imageNamed:#"camo2"]] isEqual:
[UIColor colorWithPatternImage:[UIImage imageNamed:#"camo2"]]]
Always returns false, whether I use == or isEqual. Does anybody know if it's possible to properly compare colorWithPatternImages, or CGPatterns I suppose? I've also tried comparing CGColorGetPattern(color.CGColor) but that doesn't work either.
EDIT: The reason for this is I have a function that accepts a UIColor and gives me an NSString for displaying to the user.
+(NSString *)colorNameForColor:(UIColor *)color {
if ([color isEqual:[UIColor whiteColor]]) {
return #"White";
}
if ([color isEqual:[UIColor colorWithPatternImage:[UIImage imageNamed:#"camo"]]]) {
return #"Camo";
}
...
}
Is this just an insane thing to do? I suppose I could make my own object that has a color property and a colorName property...

Using Private APIs
This took some reverse engineering of CoreGraphics but I was able to find one private method _CGPatternGetImage which appears to return the image.
You'll need to include the following headers:
#include <dlfcn.h>
#import CoreGraphics;
Create a function pointer:
typedef CGImageRef (*CGPatternGetImage)(CGPatternRef pattern);
And then access the function:
-(void)comparePatterns
{
void *handle = dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", RTLD_NOW);
CGPatternGetImage getImage = (CGPatternGetImage) dlsym(handle, "CGPatternGetImage");
UIColor *aColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"pattern1"]];
UIColor *bColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"pattern1"]];
UIColor *cColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"pattern2"]];
NSData *aImageData = UIImagePNGRepresentation([UIImage imageWithCGImage:getImage(CGColorGetPattern(aColor.CGColor))]);
NSData *bImageData = UIImagePNGRepresentation([UIImage imageWithCGImage:getImage(CGColorGetPattern(bColor.CGColor))]);
NSData *cImageData = UIImagePNGRepresentation([UIImage imageWithCGImage:getImage(CGColorGetPattern(cColor.CGColor))]);
NSLog(#"Should be true: %d",[aImageData isEqual:bImageData]);
NSLog(#"Should be false: %d",[aImageData isEqual:cImageData]);
}
You probably don't want to access any private APIs in a production app but this might be useful for testing.
Using Associative References
If this is going on the App Store then a better solution could be creating a category for UIColor and give it an associative reference to store the pattern name or whatever is easiest for you to compare. This won't compare the actual images at all so it's possible that if you don't set the correct data to identify the pattern the comparison won't be accurate.
Include the header:
#import <objc/runtime.h>
Create the category:
#interface UIColor(CustomPatterns)
#property (strong, nonatomic) NSString* patternName;
#end
#implementation UIColor(CustomPatterns)
static char CUSTOM_PATTERNS_PATTERN_NAME_KEY;
#dynamic patternName;
-(void)setPatternName:(NSString *)patternName
{
objc_setAssociatedObject(self, &CUSTOM_PATTERNS_PATTERN_NAME_KEY, patternName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)patternName
{
return (NSString*)objc_getAssociatedObject(self, &CUSTOM_PATTERNS_PATTERN_NAME_KEY);
}
#end
And then you can set your custom data and compare:
-(void)comparePatterns
{
UIColor *aColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"1"]];
aColor.patternName = #"1";
UIColor *bColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"1"]];
bColor.patternName = #"1";
UIColor *cColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"2"]];
cColor.patternName = #"2";
NSLog(#"Should be true: %d",[aColor.patternName isEqualToString:bColor.patternName]);
NSLog(#"Should be false: %d",[aColor.patternName isEqualToString:cColor.patternName]);
}

Related

How to generate a random UIColor?

I'm trying to generate a random colour from 8 options. All of the stack overflow posts / tutorials I've found have been ANY random colour. In my prefix.pch I defined 8 different sets of colour definitions this is a single example:
#define cola1 209/255.
#define colb1 0/255.
#define colc1 0/255.
#define cold1 1.0/255.
Defining different colour values for cola1-8, colb1-8, colc1-8, and cold1-8.
Then I set up a random number generator:
int randomNumber;
randomNumber = arc4random() %8;
randomNumber = randomNumber + 1;
whatRandomNumberIs = randomNumber;
I then tried setting up an [NSString stringWithFormat:#"cola%i", randomNumber]; inside the [UIColor colorWithRed etc]
like this:
[UIColor colorWithRed:[NSString stringWithFormat:#"cola%i", whatRandomNumberIs] green:[NSString stringWithFormat:#"colb%i", whatRandomNumberIs] blue:[NSString stringWithFormat:#"colc%i", whatRandomNumberIs] alpha:[NSString stringWithFormat:#"cold%i", whatRandomNumberIs]];
But then realised you cannot put an NSString in a CGFloat.
So now I'm stuck. How would I go about installing a random number from 1-8 inside the red, green, blue and alpha values without doing an NSString stringWithFormat? Is there another way to return a random UIColor value that is defined because I only want it to be specific colours??
Below is what you can do...
In prefix.pch you have as below.
#define colorCombination1 [UIColor colorWithRed:.... alpha:1.0];
#define colorCombination2 [UIColor colorWithRed:.... alpha:1.0];
#define colorCombination3 [UIColor colorWithRed:.... alpha:1.0];
#define colorCombination4 [UIColor colorWithRed:.... alpha:1.0];
#define colorCombination5 [UIColor colorWithRed:.... alpha:1.0];
#define colorCombination6 [UIColor colorWithRed:.... alpha:1.0];
#define colorCombination7 [UIColor colorWithRed:.... alpha:1.0];
#define colorCombination8 [UIColor colorWithRed:.... alpha:1.0];
Now you create array of this colors..
NSArray *myColorArray = [[NSArray alloc] initWithObjects:colorCombination1, colorCombination2, colorCombination3, colorCombination4, colorCombination5, colorCombination6, colorCombination7, colorCombination8, nil];
Now you get random number say variable as generatedRandomNumber.
UIColor *myRandomColor = [myColorArray objectAtIndex:generatedRandomNumber%8];
generatedRandomNumber%8 will give you remainder from the generatedRandomNumber.
Hope this is what you want.
Way you can get random color is by using hue , saturation and brightness
//random color
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
You are trying to construct a string at runtime and then use it as the name of a macro that was defined at compile-time. That doesn't work. No information about the name of a compile-time macro is available at runtime.
Here is one correct way to choose a random color from a set defined at compile time. Define a method to return a random color, in a category on UIColor:
#interface UIColor (Liam_RandomColor)
+ (UIColor *)Liam_randomColor;
#end
Implement the method to first (one time only) initialize an array of the predefined colors, and second (every time) to return an element of the array at random:
#implementation UIColor (Liam_RandomColor)
+ (UIColor *)Liam_randomColor {
static dispatch_once_t onceToken;
static NSArray *colors;
dispatch_once(&onceToken, ^{
colors = #[
[UIColor colorWithRed:209/255.0 green:0 blue:0 alpha:1/255.0],
[UIColor colorWithRed:50/255.0 green:100/255.0 blue:100/255.0 alpha:1],
// etc.
];
});
return colors[arc4random_uniform(colors.count)];
}
#end
You could create a category on UIColor and wrap your predefined colors in a method, something similar to this:
#interface UIColor (myCategory)
+ (UIColor *)randomColorForInt(int n);
#end
#implementation
+ (UIColor *)randomColorForInt(int n) {
if (n == 0) {
return [UIColor colorWithRed:cola1 green:colb1 blue:colc1 alpha:cold1]];
}
...
}
#end

Where should I store 30+ UIColors for quick reference?

I want to have 30+ constant UIColors so I can easily access them in my app. I'd like to be able to do something like this:
[self setBackgroundColor:[UIColor skyColor]];
[self setBackgroundColor:[UIColor dirtColor]];
[self setBackgroundColor:[UIColor yankeesColor]];
How can I do this?
Thanks!!
Define a category for UIColor:
In UIColor+MyColors.h:
#interface UIColor (MyColors)
+ (UIColor *)skyColor;
+ (UIColor *)dirtColor;
// and the rest of them
#end
In UIColor+MyColors.m:
#implementation UIColor (MyColors)
+ (UIColor *)skyColor {
static UIColor color = nil;
if (!color) {
// replace r, g, and b with the proper values
color = [UIColor colorWithRed:r green:g blue:b alpha:1];
}
return color;
}
+ (UIColor *)dirtColor {
static UIColor color = nil;
if (!color) {
// replace r, g, and b with the proper values
color = [UIColor colorWithRed:r green:g blue:b alpha:1];
}
return color;
}
// and the rest
#end
Edit:
As Martin R points out, a more modern approach to initializing the static color variable would be:
+ (UIColor *)skyColor {
static UIColor color = nil;
static dispatch_once_t predicate = 0;
dispatch_once(&predicate, ^{
// replace r, g, and b with the proper values
color = [UIColor colorWithRed:r green:g blue:b alpha:1];
});
return color;
}
This may actually be overkill in this case since there is no bad side-effect if two threads happen to initialize the nil static variable at the same time using the original code. But it is a better habit to use dispatch_once.
You can add lines like this:
#define kFirstColor [UIColor whiteColor]
#define kSecondColor [UIColor colorWithRed:100.0/255 green:100.0/255 blue:100.0/255 alpha:1.0]
At the beginning of a class or add a Color.h header to your project and import it when needed.
#import "Color.h"
Then you can use your custom colors this way:
self.view.backgroundColor = kSecondColor;

Communication between objects - C4Framework

I'm working with the alpha release of C4 and I'm trying to send messages between objects but i cant make it work. Im trying with a really simple example but i cant make it work... I've tried this:
[ashape listenFor:#"touch" from:anothershape andRunMethod:#"receive"];
but I dont get any messages or nothing...
this is what i have:
#import "MyShape.h"
#implementation MyShape
-(void)receive {
C4Log(#"this button");
}
#end
I see one main problem with the code you posted.
By default, all visible objects in C4 post a touchesBegan notification when they are tapped. In your code you are listening for #"touch" whereas #"touchesBegan" is what you should be listening for.
The changing color method is easy to implement... In your MyShape.m file, you can use a method like:
-(void)changeColor {
CGFloat red = RGBToFloat([C4Math randomInt:255]);
CGFloat green = RGBToFloat([C4Math randomInt:255]);
CGFloat blue = RGBToFloat([C4Math randomInt:255]);
self.fillColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0f];
}
In order to get things working nicely, your C4WorkSpace.m should look like:
#import "C4WorkSpace.h"
#import "MyShape.h"
#implementation C4WorkSpace {
MyShape *s1, *s2;
}
-(void)setup {
s1 = [MyShape new];
s2 = [MyShape new];
[s1 rect:CGRectMake(100, 100, 100, 100)];
[s2 rect:CGRectMake(300, 100, 100, 100)];
[s1 listenFor:#"touchesBegan" fromObject:s2 andRunMethod:#"changeColor"];
[s2 listenFor:#"touchesBegan" fromObject:s1 andRunMethod:#"changeColor"];
[self.canvas addShape:s1];
[self.canvas addShape:s2];
}
#end

Issue with [UIColor getWhite:alpha:]

I'm trying to use get the white value of a UIColor by the following (redColor is just for example):
UIColor *col = [UIColor redColor];
CGFloat *white;
if([col getWhite:white alpha:nil])
{
NSLog(#"worked");
}
else
{
NSLog(#"didn't");
}
But this always prints "didn't", and I don't understand why. UIColor.h's definition says "If the receiver is of a compatible color space, any non-NULL parameters are populated and 'YES' is returned. Otherwise, the parameters are left unchanged and 'NO' is returned." so I'm presuming that the receiver is of a non-compatible color space.... But I don't know what that means. Any ideas?
You are passing a pointer to a random piece of memory (CGFloat *white;)
You should create a static CGFloat and pass a reference to it
UIColor *col = [UIColor redColor];
CGFloat white;
if([col getWhite:&white alpha:nil])
{
NSLog(#"worked");
}
else
{
NSLog(#"didn't");
}
It is possible that it will not be able to convert from grayscale, you can check by trying
UIColor *col = [UIColor colorWithWhite:1.0 alpha:1.0];
CGFloat white;
if([col getWhite:&white alpha:nil])
{
NSLog(#"worked");
}
else
{
NSLog(#"didn't");
}

This objective C code does not change the color of label text

Debugger show some value in color . Please what am I doing wrong
#synthesize textLabel;
#synthesize textField;
#synthesize sliderRed;
#synthesize sliderGreen;
#synthesize sliderBlue;
- (void)dealloc
{
[textLabel release];
[super dealloc];
}
- (IBAction)updateLabel
{
NSString * textValue = [textField text];
float red = [sliderRed value]/255.f;
float green = [sliderGreen value]/255.f;
float blue = [sliderBlue value]/255.f;
UIColor *textColour = [[UIColor alloc]initWithRed:red green:green blue:blue alpha:1.0];
[textLabel setText:textValue];
[textLabel setTextColor:textColour];
}
I guess sliderRed, sliderGreen and sliderBlue are UISlider instances? What are their min/max values? If you left it at default 0.0 to 1.0 then this code would give you some really low values:
float red = [sliderRed value]/255.f;
float green = [sliderGreen value]/255.f;
float blue = [sliderBlue value]/255.f;
UIColor's method you use takes float parameters from 0.0 to 1.0 so simply passing it the slider values without dividing them would work.
And don't forget to put [textColour release]; at the end of that method or you will be leaking a new UIColor instance every time the method gets called.
Nothing wrong on your codes. maybe something wrong in other places.
Try to replace this line:
UIColor *textColour = [[UIColor alloc]initWithRed:red green:green blue:blue alpha:1.0];
to
UIColor *textColour = [UIColor redColor];
And check if the redColor works. if not, so it might something wrong in your other codes.
Try adding [textLabel setNeeedsDisplay] after you've changed the drawing attributes.

Resources