├── .gitignore ├── Icon.png ├── Default.png ├── helloworld.png ├── HelloWorld_Prefix.pch ├── Info.plist ├── UIColor-Expanded.h ├── main.m ├── HelloWorld.xcodeproj └── project.pbxproj └── UIColor-Expanded.m /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcodeproj/* 2 | !*.xcodeproj/project.pbxproj 3 | /build 4 | -------------------------------------------------------------------------------- /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilyball/uicolor-utilities/HEAD/Icon.png -------------------------------------------------------------------------------- /Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilyball/uicolor-utilities/HEAD/Default.png -------------------------------------------------------------------------------- /helloworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilyball/uicolor-utilities/HEAD/helloworld.png -------------------------------------------------------------------------------- /HelloWorld_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'HelloWorld' target in the 'HelloWorld' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.sadun.${EXECUTABLE_NAME} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /UIColor-Expanded.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #define SUPPORTS_UNDOCUMENTED_API 0 4 | 5 | @interface UIColor (UIColor_Expanded) 6 | @property (nonatomic, readonly) CGColorSpaceModel colorSpaceModel; 7 | @property (nonatomic, readonly) BOOL canProvideRGBComponents; 8 | 9 | // With the exception of -alpha, these properties will function 10 | // correctly only if this color is an RGB or white color. 11 | // In these cases, canProvideRGBComponents returns YES. 12 | @property (nonatomic, readonly) CGFloat red; 13 | @property (nonatomic, readonly) CGFloat green; 14 | @property (nonatomic, readonly) CGFloat blue; 15 | @property (nonatomic, readonly) CGFloat white; 16 | @property (nonatomic, readonly) CGFloat hue; 17 | @property (nonatomic, readonly) CGFloat saturation; 18 | @property (nonatomic, readonly) CGFloat brightness; 19 | @property (nonatomic, readonly) CGFloat alpha; 20 | @property (nonatomic, readonly) CGFloat luminance; 21 | @property (nonatomic, readonly) UInt32 rgbHex; 22 | 23 | - (NSString *)colorSpaceString; 24 | - (NSArray *)arrayFromRGBAComponents; 25 | 26 | // Bulk access to RGB and HSB components of the color 27 | // HSB components are converted from the RGB components 28 | - (BOOL)red:(CGFloat *)r green:(CGFloat *)g blue:(CGFloat *)b alpha:(CGFloat *)a; 29 | - (BOOL)hue:(CGFloat *)h saturation:(CGFloat *)s brightness:(CGFloat *)b alpha:(CGFloat *)a; 30 | 31 | // Return a grey-scale representation of the color 32 | - (UIColor *)colorByLuminanceMapping; 33 | 34 | // Arithmetic operations on the color 35 | - (UIColor *)colorByMultiplyingByRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 36 | - (UIColor *) colorByAddingRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 37 | - (UIColor *) colorByLighteningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 38 | - (UIColor *) colorByDarkeningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 39 | 40 | - (UIColor *)colorByMultiplyingBy:(CGFloat)f; 41 | - (UIColor *) colorByAdding:(CGFloat)f; 42 | - (UIColor *) colorByLighteningTo:(CGFloat)f; 43 | - (UIColor *) colorByDarkeningTo:(CGFloat)f; 44 | 45 | - (UIColor *)colorByMultiplyingByColor:(UIColor *)color; 46 | - (UIColor *) colorByAddingColor:(UIColor *)color; 47 | - (UIColor *) colorByLighteningToColor:(UIColor *)color; 48 | - (UIColor *) colorByDarkeningToColor:(UIColor *)color; 49 | 50 | // Related colors 51 | - (UIColor *)contrastingColor; // A good contrasting color: will be either black or white 52 | - (UIColor *)complementaryColor; // A complementary color that should look good with this color 53 | - (NSArray*)triadicColors; // Two colors that should look good with this color 54 | - (NSArray*)analogousColorsWithStepAngle:(CGFloat)stepAngle pairCount:(int)pairs; // Multiple pairs of colors 55 | 56 | // String representations of the color 57 | - (NSString *)stringFromColor; 58 | - (NSString *)hexStringFromColor; 59 | 60 | // The named color that matches this one most closely 61 | - (NSString *)closestColorName; 62 | 63 | // Color builders 64 | + (UIColor *)randomColor; 65 | + (UIColor *)colorWithString:(NSString *)stringToConvert; 66 | + (UIColor *)colorWithRGBHex:(UInt32)hex; 67 | + (UIColor *)colorWithHexString:(NSString *)stringToConvert; 68 | + (UIColor *)colorWithName:(NSString *)cssColorName; 69 | 70 | // Return a dictionary mapping color names to colors. 71 | // The named are from the css3 color specification. 72 | + (NSDictionary *)namedColors; 73 | 74 | // Build a color with the given HSB values 75 | + (UIColor *)colorWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha; 76 | 77 | // Low level conversions between RGB and HSL spaces 78 | + (void)hue:(CGFloat)h saturation:(CGFloat)s brightness:(CGFloat)v toRed:(CGFloat *)r green:(CGFloat *)g blue:(CGFloat *)b; 79 | + (void)red:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b toHue:(CGFloat *)h saturation:(CGFloat *)s brightness:(CGFloat *)v; 80 | 81 | @end 82 | 83 | #if SUPPORTS_UNDOCUMENTED_API 84 | // UIColor_Undocumented_Expanded 85 | // Methods which rely on undocumented methods of UIColor 86 | @interface UIColor (UIColor_Undocumented_Expanded) 87 | - (NSString *)fetchStyleString; 88 | - (UIColor *)rgbColor; // Via Poltras 89 | @end 90 | #endif // SUPPORTS_UNDOCUMENTED_API 91 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "UIColor-Expanded.h" 3 | 4 | #define INFO 100 5 | 6 | #define RED_SLIDER 101 7 | #define GREEN_SLIDER 102 8 | #define BLUE_SLIDER 103 9 | 10 | #define CLOSEST_COLOR 150 11 | #define COMPLEMENTARY_COLOR 151 12 | #define CONTRASTING_COLOR 152 13 | #define TRIADICA_COLOR 153 14 | #define TRIADICB_COLOR 154 15 | 16 | @interface HelloController : UIViewController { 17 | UIColor *bgcolor; 18 | } 19 | @property (nonatomic, retain) UIColor *bgcolor; 20 | @end 21 | 22 | @implementation HelloController 23 | @synthesize bgcolor; 24 | 25 | - (void)getInfo { 26 | self.title = [self.bgcolor hexStringFromColor]; 27 | // self.bgcolor = [UIColor colorWithString:[self.bgcolor stringFromColor]]; 28 | // self.title = [self.bgcolor stringFromColor]; 29 | // printf("%f\n", self.bgcolor.green); 30 | } 31 | 32 | - (void)update:(UISlider *)aSlider { 33 | float r, g, b; 34 | 35 | // Get rgb from the sliders 36 | r = [(UISlider *)[self.view viewWithTag:RED_SLIDER] value]; 37 | g = [(UISlider *)[self.view viewWithTag:GREEN_SLIDER] value]; 38 | b = [(UISlider *)[self.view viewWithTag:BLUE_SLIDER] value]; 39 | 40 | UIColor* color = [UIColor colorWithRed:r green:g blue:b alpha:1.0f]; 41 | UIColor* contrastingColor = [color contrastingColor]; 42 | UIColor* complementaryColor = [color complementaryColor]; 43 | 44 | CGFloat h,s,v; 45 | [color hue:&h saturation:&s brightness:&v alpha:nil]; 46 | 47 | CGFloat xh,xs,xv; 48 | [contrastingColor hue:&xh saturation:&xs brightness:&xv alpha:nil]; 49 | 50 | CGFloat ch,cs,cv; 51 | [complementaryColor hue:&ch saturation:&cs brightness:&cv alpha:nil]; 52 | 53 | self.bgcolor = color; 54 | [self.view setBackgroundColor:bgcolor]; 55 | 56 | // Set the title to the hex string 57 | self.title = [NSString stringWithFormat:@"#%@", [color hexStringFromColor]]; 58 | 59 | // Set the info area 60 | UILabel* l; 61 | l = (UILabel *)[self.view viewWithTag:INFO]; 62 | l.text = [NSString stringWithFormat: 63 | @"h=%5.1f s=%4.2f b=%4.2f (color)\n" 64 | "h=%5.1f s=%4.2f b=%4.2f (xtrst)\n" 65 | "h=%5.1f s=%4.2f b=%4.2f (compl)\n" 66 | , 67 | h, s, v, 68 | xh, xs, xv, 69 | ch, cs, cv 70 | ]; 71 | 72 | // Set the background for each slider to the pure color selected by that slider 73 | int mask = 0x00ff0000; 74 | for (int s = RED_SLIDER; s <= BLUE_SLIDER; ++s) { 75 | UISlider *slider = (UISlider *)[self.view viewWithTag:s]; 76 | UIColor *sliderColor = [UIColor colorWithRGBHex:color.rgbHex & mask]; 77 | slider.backgroundColor = sliderColor; 78 | mask >>= 8; 79 | } 80 | 81 | // Set the closest color 82 | NSString* closestColorName = [color closestColorName]; 83 | UIColor* closestColor = [UIColor colorWithName:closestColorName]; 84 | 85 | l = (UILabel *)[self.view viewWithTag:CLOSEST_COLOR]; 86 | l.text = [NSString stringWithFormat:@"%@ #%@", closestColorName, closestColor.hexStringFromColor]; 87 | l.backgroundColor = closestColor; 88 | l.textColor = [closestColor contrastingColor]; 89 | 90 | // Set the contrasting color 91 | l = (UILabel *)[self.view viewWithTag:CONTRASTING_COLOR]; 92 | l.text = [NSString stringWithFormat:@"Contrasting #%@", contrastingColor.hexStringFromColor]; 93 | l.backgroundColor = contrastingColor; 94 | l.textColor = color; 95 | 96 | // Set the complementary color 97 | l = (UILabel *)[self.view viewWithTag:COMPLEMENTARY_COLOR]; 98 | l.text = [NSString stringWithFormat:@"Complementary #%@", complementaryColor.hexStringFromColor]; 99 | l.backgroundColor = complementaryColor; 100 | l.textColor = [complementaryColor contrastingColor]; 101 | 102 | // Set the triadic colorS 103 | NSArray* triadics = [color triadicColors]; 104 | 105 | l = (UILabel *)[self.view viewWithTag:TRIADICA_COLOR]; 106 | UIColor* t = [triadics objectAtIndex:0]; 107 | l.text = [NSString stringWithFormat:@"Triadic #%@", t.hexStringFromColor]; 108 | l.backgroundColor = t; 109 | l.textColor = t.contrastingColor; 110 | 111 | 112 | l = (UILabel *)[self.view viewWithTag:TRIADICB_COLOR]; 113 | t = [triadics objectAtIndex:1]; 114 | l.text = [NSString stringWithFormat:@"Triadic #%@", t.hexStringFromColor]; 115 | l.backgroundColor = t; 116 | l.textColor = t.contrastingColor; 117 | } 118 | 119 | - (void)loadView { 120 | UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; 121 | self.view = contentView; 122 | 123 | 124 | // Add a right button 125 | /* 126 | self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] 127 | initWithTitle:@"Action" 128 | style:UIBarButtonItemStylePlain 129 | target:self 130 | action:@selector(getInfo)] autorelease]; 131 | */ 132 | 133 | 134 | // Create info area 135 | UILabel *infoArea = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 60.0f)]; 136 | infoArea.center = CGPointMake(160.f, 30.0f); 137 | infoArea.tag = INFO; 138 | infoArea.numberOfLines = 0; 139 | infoArea.font = [UIFont fontWithName:@"Courier New" size:10.0f]; 140 | [contentView addSubview:infoArea]; 141 | [infoArea release]; 142 | 143 | // Create three sliders 144 | UISlider *s1 = [[UISlider alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)]; 145 | s1.center = CGPointMake(160.0f, 80.0f); 146 | s1.maximumValue = 1.0f; 147 | s1.minimumValue = 0.0f; 148 | s1.value = 1.0f; 149 | s1.tag = RED_SLIDER; 150 | s1.backgroundColor = [UIColor redColor]; 151 | [s1 addTarget:self action:@selector(update:) forControlEvents:UIControlEventValueChanged]; 152 | [contentView addSubview:s1]; 153 | [s1 release]; 154 | 155 | UISlider *s2 = [[UISlider alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)]; 156 | s2.center = CGPointMake(160.0f, 110.0f); 157 | s2.maximumValue = 1.0f; 158 | s2.minimumValue = 0.0f; 159 | s2.value = 1.0f; 160 | s2.tag = GREEN_SLIDER; 161 | s2.backgroundColor = [UIColor greenColor]; 162 | [s2 addTarget:self action:@selector(update:) forControlEvents:UIControlEventValueChanged]; 163 | [contentView addSubview:s2]; 164 | [s2 release]; 165 | 166 | UISlider *s3 = [[UISlider alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)]; 167 | s3.center = CGPointMake(160.0f, 140.0f); 168 | s3.maximumValue = 1.0f; 169 | s3.minimumValue = 0.0f; 170 | s3.value = 1.0f; 171 | s3.tag = BLUE_SLIDER; 172 | s3.backgroundColor = [UIColor blueColor]; 173 | [s3 addTarget:self action:@selector(update:) forControlEvents:UIControlEventValueChanged]; 174 | [contentView addSubview:s3]; 175 | [s3 release]; 176 | 177 | // Create closest color well 178 | UILabel *closestColor = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)]; 179 | closestColor.center = CGPointMake(160.f, 260.0f); 180 | closestColor.tag = CLOSEST_COLOR; 181 | [contentView addSubview:closestColor]; 182 | [closestColor release]; 183 | 184 | // Create contrasting color well 185 | UILabel *contrastingColor = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)]; 186 | contrastingColor.center = CGPointMake(160.f, 290.0f); 187 | contrastingColor.tag = CONTRASTING_COLOR; 188 | [contentView addSubview:contrastingColor]; 189 | [contrastingColor release]; 190 | 191 | // Create complementary color well 192 | UILabel *complementaryColor = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 30.0f)]; 193 | complementaryColor.center = CGPointMake(160.f, 320.0f); 194 | complementaryColor.tag = COMPLEMENTARY_COLOR; 195 | [contentView addSubview:complementaryColor]; 196 | [complementaryColor release]; 197 | 198 | // Create triadic color wells 199 | UILabel *traidicA = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 160.0f, 30.0f)]; 200 | traidicA.center = CGPointMake(80.0f, 360.0f); 201 | traidicA.tag = TRIADICA_COLOR; 202 | [contentView addSubview:traidicA]; 203 | [traidicA release]; 204 | 205 | UILabel *traidicB = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 160.0f, 30.0f)]; 206 | traidicB.center = CGPointMake(240.0f, 360.0f); 207 | traidicB.tag = TRIADICB_COLOR; 208 | [contentView addSubview:traidicB]; 209 | [traidicB release]; 210 | 211 | // Update the view 212 | [self update:s1]; 213 | 214 | [contentView release]; 215 | } 216 | @end 217 | 218 | 219 | @interface SampleAppDelegate : NSObject 220 | @end 221 | 222 | @implementation SampleAppDelegate 223 | - (void)applicationDidFinishLaunching:(UIApplication *)application { 224 | UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 225 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[HelloController alloc] init]]; 226 | [window addSubview:nav.view]; 227 | [window makeKeyAndVisible]; 228 | } 229 | @end 230 | 231 | int main(int argc, char *argv[]) { 232 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 233 | int retVal = UIApplicationMain(argc, argv, nil, @"SampleAppDelegate"); 234 | [pool release]; 235 | return retVal; 236 | } 237 | -------------------------------------------------------------------------------- /HelloWorld.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */; }; 11 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 12 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 13 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 14 | 8E3143A20D8EFC7900101D4D /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E31439F0D8EFC7900101D4D /* Default.png */; }; 15 | 8E3143A30D8EFC7900101D4D /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E3143A00D8EFC7900101D4D /* Icon.png */; }; 16 | 8E3143A40D8EFC7900101D4D /* helloworld.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E3143A10D8EFC7900101D4D /* helloworld.png */; }; 17 | 8ED61B4A0F3CA54400B2FE57 /* UIColor-Expanded.m in Sources */ = {isa = PBXBuildFile; fileRef = 8ED61B490F3CA54400B2FE57 /* UIColor-Expanded.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 22 | 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 23 | 1D6058910D05DD3D006BFB54 /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 25 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 26 | 32CA4F630368D1EE00C91783 /* HelloWorld_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HelloWorld_Prefix.pch; sourceTree = ""; }; 27 | 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | 8E31439F0D8EFC7900101D4D /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 29 | 8E3143A00D8EFC7900101D4D /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; 30 | 8E3143A10D8EFC7900101D4D /* helloworld.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = helloworld.png; sourceTree = ""; }; 31 | 8ED61B480F3CA54400B2FE57 /* UIColor-Expanded.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor-Expanded.h"; sourceTree = ""; }; 32 | 8ED61B490F3CA54400B2FE57 /* UIColor-Expanded.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor-Expanded.m"; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 41 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 42 | 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */, 53 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 54 | 1D30AB110D05D00D00671497 /* Foundation.framework */, 55 | ); 56 | name = "Linked Frameworks"; 57 | sourceTree = ""; 58 | }; 59 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | ); 63 | name = "Other Frameworks"; 64 | sourceTree = ""; 65 | }; 66 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 1D6058910D05DD3D006BFB54 /* HelloWorld.app */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 8E3143A50D8EFC8A00101D4D /* Images */, 78 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 79 | 29B97317FDCFA39411CA2CEA /* Resources */, 80 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 81 | 19C28FACFE9D520D11CA2CBB /* Products */, 82 | ); 83 | name = CustomTemplate; 84 | sourceTree = ""; 85 | }; 86 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 8ED61B480F3CA54400B2FE57 /* UIColor-Expanded.h */, 90 | 8ED61B490F3CA54400B2FE57 /* UIColor-Expanded.m */, 91 | 32CA4F630368D1EE00C91783 /* HelloWorld_Prefix.pch */, 92 | 29B97316FDCFA39411CA2CEA /* main.m */, 93 | ); 94 | name = "Other Sources"; 95 | sourceTree = ""; 96 | }; 97 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 8D1107310486CEB800E47090 /* Info.plist */, 101 | ); 102 | name = Resources; 103 | sourceTree = ""; 104 | }; 105 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 109 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, 110 | ); 111 | name = Frameworks; 112 | sourceTree = ""; 113 | }; 114 | 8E3143A50D8EFC8A00101D4D /* Images */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 8E31439F0D8EFC7900101D4D /* Default.png */, 118 | 8E3143A00D8EFC7900101D4D /* Icon.png */, 119 | 8E3143A10D8EFC7900101D4D /* helloworld.png */, 120 | ); 121 | name = Images; 122 | sourceTree = ""; 123 | }; 124 | /* End PBXGroup section */ 125 | 126 | /* Begin PBXNativeTarget section */ 127 | 1D6058900D05DD3D006BFB54 /* HelloWorld */ = { 128 | isa = PBXNativeTarget; 129 | buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloWorld" */; 130 | buildPhases = ( 131 | 1D60588D0D05DD3D006BFB54 /* Resources */, 132 | 1D60588E0D05DD3D006BFB54 /* Sources */, 133 | 1D60588F0D05DD3D006BFB54 /* Frameworks */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | ); 139 | name = HelloWorld; 140 | productName = HelloWorld; 141 | productReference = 1D6058910D05DD3D006BFB54 /* HelloWorld.app */; 142 | productType = "com.apple.product-type.application"; 143 | }; 144 | /* End PBXNativeTarget section */ 145 | 146 | /* Begin PBXProject section */ 147 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 148 | isa = PBXProject; 149 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "HelloWorld" */; 150 | compatibilityVersion = "Xcode 3.1"; 151 | hasScannedForEncodings = 1; 152 | mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; 153 | projectDirPath = ""; 154 | projectRoot = ""; 155 | targets = ( 156 | 1D6058900D05DD3D006BFB54 /* HelloWorld */, 157 | ); 158 | }; 159 | /* End PBXProject section */ 160 | 161 | /* Begin PBXResourcesBuildPhase section */ 162 | 1D60588D0D05DD3D006BFB54 /* Resources */ = { 163 | isa = PBXResourcesBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | 8E3143A20D8EFC7900101D4D /* Default.png in Resources */, 167 | 8E3143A30D8EFC7900101D4D /* Icon.png in Resources */, 168 | 8E3143A40D8EFC7900101D4D /* helloworld.png in Resources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXResourcesBuildPhase section */ 173 | 174 | /* Begin PBXSourcesBuildPhase section */ 175 | 1D60588E0D05DD3D006BFB54 /* Sources */ = { 176 | isa = PBXSourcesBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */, 180 | 8ED61B4A0F3CA54400B2FE57 /* UIColor-Expanded.m in Sources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXSourcesBuildPhase section */ 185 | 186 | /* Begin XCBuildConfiguration section */ 187 | 1D6058940D05DD3E006BFB54 /* Debug */ = { 188 | isa = XCBuildConfiguration; 189 | buildSettings = { 190 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Erica Sadun"; 191 | COPY_PHASE_STRIP = NO; 192 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 193 | GCC_DYNAMIC_NO_PIC = NO; 194 | GCC_ENABLE_FIX_AND_CONTINUE = NO; 195 | GCC_OPTIMIZATION_LEVEL = 0; 196 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 197 | GCC_PREFIX_HEADER = HelloWorld_Prefix.pch; 198 | INFOPLIST_FILE = Info.plist; 199 | PREBINDING = NO; 200 | PRODUCT_NAME = HelloWorld; 201 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = "A2FA5C45-7EC1-4FE4-A834-590BCF399409"; 202 | }; 203 | name = Debug; 204 | }; 205 | 1D6058950D05DD3E006BFB54 /* Release */ = { 206 | isa = XCBuildConfiguration; 207 | buildSettings = { 208 | COPY_PHASE_STRIP = YES; 209 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 210 | GCC_ENABLE_FIX_AND_CONTINUE = NO; 211 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 212 | GCC_PREFIX_HEADER = HelloWorld_Prefix.pch; 213 | INFOPLIST_FILE = Info.plist; 214 | PREBINDING = NO; 215 | PRODUCT_NAME = HelloWorld; 216 | WRAPPER_EXTENSION = app; 217 | }; 218 | name = Release; 219 | }; 220 | C01FCF4F08A954540054247B /* Debug */ = { 221 | isa = XCBuildConfiguration; 222 | buildSettings = { 223 | ALWAYS_SEARCH_USER_PATHS = NO; 224 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 225 | CODE_SIGN_IDENTITY = "Erica Sadun"; 226 | "CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = "iPhone Developer"; 227 | GCC_C_LANGUAGE_STANDARD = c99; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 229 | GCC_WARN_UNUSED_VARIABLE = YES; 230 | ONLY_ACTIVE_ARCH = YES; 231 | PREBINDING = NO; 232 | PRODUCT_NAME = "Oh Hello"; 233 | SDKROOT = iphoneos2.0; 234 | }; 235 | name = Debug; 236 | }; 237 | C01FCF5008A954540054247B /* Release */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 241 | PREBINDING = NO; 242 | SDKROOT = iphoneos2.0; 243 | }; 244 | name = Release; 245 | }; 246 | /* End XCBuildConfiguration section */ 247 | 248 | /* Begin XCConfigurationList section */ 249 | 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloWorld" */ = { 250 | isa = XCConfigurationList; 251 | buildConfigurations = ( 252 | 1D6058940D05DD3E006BFB54 /* Debug */, 253 | 1D6058950D05DD3E006BFB54 /* Release */, 254 | ); 255 | defaultConfigurationIsVisible = 0; 256 | defaultConfigurationName = Release; 257 | }; 258 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "HelloWorld" */ = { 259 | isa = XCConfigurationList; 260 | buildConfigurations = ( 261 | C01FCF4F08A954540054247B /* Debug */, 262 | C01FCF5008A954540054247B /* Release */, 263 | ); 264 | defaultConfigurationIsVisible = 0; 265 | defaultConfigurationName = Release; 266 | }; 267 | /* End XCConfigurationList section */ 268 | }; 269 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 270 | } 271 | -------------------------------------------------------------------------------- /UIColor-Expanded.m: -------------------------------------------------------------------------------- 1 | #import "UIColor-Expanded.h" 2 | 3 | /* 4 | 5 | Thanks to Poltras, Millenomi, Eridius, Nownot, WhatAHam, jberry, 6 | and everyone else who helped out but whose name is inadvertantly omitted 7 | 8 | */ 9 | 10 | /* 11 | Current outstanding request list: 12 | 13 | - PolarBearFarm - color descriptions ([UIColor warmGrayWithHintOfBlueTouchOfRedAndSplashOfYellowColor]) 14 | - Crayola color set 15 | - Eridius - UIColor needs a method that takes 2 colors and gives a third complementary one 16 | - Consider UIMutableColor that can be adjusted (brighter, cooler, warmer, thicker-alpha, etc) 17 | */ 18 | 19 | /* 20 | FOR REFERENCE: Color Space Models: enum CGColorSpaceModel { 21 | kCGColorSpaceModelUnknown = -1, 22 | kCGColorSpaceModelMonochrome, 23 | kCGColorSpaceModelRGB, 24 | kCGColorSpaceModelCMYK, 25 | kCGColorSpaceModelLab, 26 | kCGColorSpaceModelDeviceN, 27 | kCGColorSpaceModelIndexed, 28 | kCGColorSpaceModelPattern 29 | }; 30 | */ 31 | 32 | static const char *colorNameDB; 33 | 34 | // Complete dictionary of color name -> color mappings, generated 35 | // by a call to +namedColors or +colorWithName: 36 | static NSDictionary *colorNameCache = nil; 37 | static NSLock *colorNameCacheLock; 38 | 39 | #if SUPPORTS_UNDOCUMENTED_API 40 | // UIColor_Undocumented 41 | // Undocumented methods of UIColor 42 | @interface UIColor (UIColor_Undocumented) 43 | - (NSString *)styleString; 44 | @end 45 | #endif // SUPPORTS_UNDOCUMENTED_API 46 | 47 | @interface UIColor (UIColor_Expanded_Support) 48 | + (void)populateColorNameCache; 49 | @end 50 | 51 | #pragma mark - 52 | 53 | @implementation UIColor (UIColor_Expanded) 54 | 55 | - (CGColorSpaceModel)colorSpaceModel { 56 | return CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor)); 57 | } 58 | 59 | - (NSString *)colorSpaceString { 60 | switch (self.colorSpaceModel) { 61 | case kCGColorSpaceModelUnknown: 62 | return @"kCGColorSpaceModelUnknown"; 63 | case kCGColorSpaceModelMonochrome: 64 | return @"kCGColorSpaceModelMonochrome"; 65 | case kCGColorSpaceModelRGB: 66 | return @"kCGColorSpaceModelRGB"; 67 | case kCGColorSpaceModelCMYK: 68 | return @"kCGColorSpaceModelCMYK"; 69 | case kCGColorSpaceModelLab: 70 | return @"kCGColorSpaceModelLab"; 71 | case kCGColorSpaceModelDeviceN: 72 | return @"kCGColorSpaceModelDeviceN"; 73 | case kCGColorSpaceModelIndexed: 74 | return @"kCGColorSpaceModelIndexed"; 75 | case kCGColorSpaceModelPattern: 76 | return @"kCGColorSpaceModelPattern"; 77 | default: 78 | return @"Not a valid color space"; 79 | } 80 | } 81 | 82 | - (BOOL)canProvideRGBComponents { 83 | switch (self.colorSpaceModel) { 84 | case kCGColorSpaceModelRGB: 85 | case kCGColorSpaceModelMonochrome: 86 | return YES; 87 | default: 88 | return NO; 89 | } 90 | } 91 | 92 | - (NSArray *)arrayFromRGBAComponents { 93 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -arrayFromRGBAComponents"); 94 | 95 | CGFloat r,g,b,a; 96 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 97 | 98 | return [NSArray arrayWithObjects: 99 | [NSNumber numberWithFloat:r], 100 | [NSNumber numberWithFloat:g], 101 | [NSNumber numberWithFloat:b], 102 | [NSNumber numberWithFloat:a], 103 | nil]; 104 | } 105 | 106 | - (BOOL)red:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha { 107 | const CGFloat *components = CGColorGetComponents(self.CGColor); 108 | 109 | CGFloat r,g,b,a; 110 | 111 | switch (self.colorSpaceModel) { 112 | case kCGColorSpaceModelMonochrome: 113 | r = g = b = components[0]; 114 | a = components[1]; 115 | break; 116 | case kCGColorSpaceModelRGB: 117 | r = components[0]; 118 | g = components[1]; 119 | b = components[2]; 120 | a = components[3]; 121 | break; 122 | default: // We don't know how to handle this model 123 | return NO; 124 | } 125 | 126 | if (red) *red = r; 127 | if (green) *green = g; 128 | if (blue) *blue = b; 129 | if (alpha) *alpha = a; 130 | 131 | return YES; 132 | } 133 | 134 | - (BOOL)hue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha { 135 | 136 | CGFloat r,g,b,a; 137 | if (![self red:&r green:&g blue:&b alpha:&a]) return NO; 138 | 139 | [UIColor red:r green:g blue:b toHue:hue saturation:saturation brightness:brightness]; 140 | 141 | if (alpha) *alpha = a; 142 | 143 | return YES; 144 | } 145 | 146 | - (CGFloat)red { 147 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -red"); 148 | const CGFloat *c = CGColorGetComponents(self.CGColor); 149 | return c[0]; 150 | } 151 | 152 | - (CGFloat)green { 153 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -green"); 154 | const CGFloat *c = CGColorGetComponents(self.CGColor); 155 | if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0]; 156 | return c[1]; 157 | } 158 | 159 | - (CGFloat)blue { 160 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -blue"); 161 | const CGFloat *c = CGColorGetComponents(self.CGColor); 162 | if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0]; 163 | return c[2]; 164 | } 165 | 166 | - (CGFloat)white { 167 | NSAssert(self.colorSpaceModel == kCGColorSpaceModelMonochrome, @"Must be a Monochrome color to use -white"); 168 | const CGFloat *c = CGColorGetComponents(self.CGColor); 169 | return c[0]; 170 | } 171 | 172 | - (CGFloat)hue { 173 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -hue"); 174 | CGFloat h = 0.0f; 175 | [self hue:&h saturation:nil brightness:nil alpha:nil]; 176 | return h; 177 | } 178 | 179 | - (CGFloat)saturation { 180 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -saturation"); 181 | CGFloat s = 0.0f; 182 | [self hue:nil saturation:&s brightness:nil alpha:nil]; 183 | return s; 184 | } 185 | 186 | - (CGFloat)brightness { 187 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -brightness"); 188 | CGFloat v = 0.0f; 189 | [self hue:nil saturation:nil brightness:&v alpha:nil]; 190 | return v; 191 | } 192 | 193 | - (CGFloat)alpha { 194 | return CGColorGetAlpha(self.CGColor); 195 | } 196 | 197 | - (CGFloat)luminance { 198 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use luminance"); 199 | 200 | CGFloat r,g,b; 201 | if (![self red:&r green:&g blue:&b alpha:nil]) return 0.0f; 202 | 203 | // http://en.wikipedia.org/wiki/Luma_(video) 204 | // Y = 0.2126 R + 0.7152 G + 0.0722 B 205 | 206 | return r*0.2126f + g*0.7152f + b*0.0722f; 207 | } 208 | 209 | - (UInt32)rgbHex { 210 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use rgbHex"); 211 | 212 | CGFloat r,g,b,a; 213 | if (![self red:&r green:&g blue:&b alpha:&a]) return 0; 214 | 215 | r = MIN(MAX(r, 0.0f), 1.0f); 216 | g = MIN(MAX(g, 0.0f), 1.0f); 217 | b = MIN(MAX(b, 0.0f), 1.0f); 218 | 219 | return (((int)roundf(r * 255)) << 16) 220 | | (((int)roundf(g * 255)) << 8) 221 | | (((int)roundf(b * 255))); 222 | } 223 | 224 | #pragma mark Arithmetic operations 225 | 226 | - (UIColor *)colorByLuminanceMapping { 227 | return [UIColor colorWithWhite:self.luminance alpha:1.0f]; 228 | } 229 | 230 | - (UIColor *)colorByMultiplyingByRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 231 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 232 | 233 | CGFloat r,g,b,a; 234 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 235 | 236 | return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r * red)) 237 | green:MAX(0.0, MIN(1.0, g * green)) 238 | blue:MAX(0.0, MIN(1.0, b * blue)) 239 | alpha:MAX(0.0, MIN(1.0, a * alpha))]; 240 | } 241 | 242 | - (UIColor *)colorByAddingRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 243 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 244 | 245 | CGFloat r,g,b,a; 246 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 247 | 248 | return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r + red)) 249 | green:MAX(0.0, MIN(1.0, g + green)) 250 | blue:MAX(0.0, MIN(1.0, b + blue)) 251 | alpha:MAX(0.0, MIN(1.0, a + alpha))]; 252 | } 253 | 254 | - (UIColor *)colorByLighteningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 255 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 256 | 257 | CGFloat r,g,b,a; 258 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 259 | 260 | return [UIColor colorWithRed:MAX(r, red) 261 | green:MAX(g, green) 262 | blue:MAX(b, blue) 263 | alpha:MAX(a, alpha)]; 264 | } 265 | 266 | - (UIColor *)colorByDarkeningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 267 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 268 | 269 | CGFloat r,g,b,a; 270 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 271 | 272 | return [UIColor colorWithRed:MIN(r, red) 273 | green:MIN(g, green) 274 | blue:MIN(b, blue) 275 | alpha:MIN(a, alpha)]; 276 | } 277 | 278 | - (UIColor *)colorByMultiplyingBy:(CGFloat)f { 279 | return [self colorByMultiplyingByRed:f green:f blue:f alpha:1.0f]; 280 | } 281 | 282 | - (UIColor *)colorByAdding:(CGFloat)f { 283 | return [self colorByMultiplyingByRed:f green:f blue:f alpha:0.0f]; 284 | } 285 | 286 | - (UIColor *)colorByLighteningTo:(CGFloat)f { 287 | return [self colorByLighteningToRed:f green:f blue:f alpha:0.0f]; 288 | } 289 | 290 | - (UIColor *)colorByDarkeningTo:(CGFloat)f { 291 | return [self colorByDarkeningToRed:f green:f blue:f alpha:1.0f]; 292 | } 293 | 294 | - (UIColor *)colorByMultiplyingByColor:(UIColor *)color { 295 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 296 | 297 | CGFloat r,g,b,a; 298 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 299 | 300 | return [self colorByMultiplyingByRed:r green:g blue:b alpha:1.0f]; 301 | } 302 | 303 | - (UIColor *)colorByAddingColor:(UIColor *)color { 304 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 305 | 306 | CGFloat r,g,b,a; 307 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 308 | 309 | return [self colorByAddingRed:r green:g blue:b alpha:0.0f]; 310 | } 311 | 312 | - (UIColor *)colorByLighteningToColor:(UIColor *)color { 313 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 314 | 315 | CGFloat r,g,b,a; 316 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 317 | 318 | return [self colorByLighteningToRed:r green:g blue:b alpha:0.0f]; 319 | } 320 | 321 | - (UIColor *)colorByDarkeningToColor:(UIColor *)color { 322 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmetic operations"); 323 | 324 | CGFloat r,g,b,a; 325 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 326 | 327 | return [self colorByDarkeningToRed:r green:g blue:b alpha:1.0f]; 328 | } 329 | 330 | #pragma mark Complementary Colors, etc 331 | 332 | // Pick a color that is likely to contrast well with this color 333 | - (UIColor *)contrastingColor { 334 | return (self.luminance > 0.5f) ? [UIColor blackColor] : [UIColor whiteColor]; 335 | } 336 | 337 | // Pick the color that is 180 degrees away in hue 338 | - (UIColor *)complementaryColor { 339 | 340 | // Convert to HSB 341 | CGFloat h,s,v,a; 342 | if (![self hue:&h saturation:&s brightness:&v alpha:&a]) return nil; 343 | 344 | // Pick color 180 degrees away 345 | h += 180.0f; 346 | if (h > 360.f) h -= 360.0f; 347 | 348 | // Create a color in RGB 349 | return [UIColor colorWithHue:h saturation:s brightness:v alpha:a]; 350 | } 351 | 352 | // Pick two colors more colors such that all three are equidistant on the color wheel 353 | // (120 degrees and 240 degress difference in hue from self) 354 | - (NSArray*)triadicColors { 355 | return [self analogousColorsWithStepAngle:120.0f pairCount:1]; 356 | } 357 | 358 | // Pick n pairs of colors, stepping in increasing steps away from this color around the wheel 359 | - (NSArray*)analogousColorsWithStepAngle:(CGFloat)stepAngle pairCount:(int)pairs { 360 | // Convert to HSB 361 | CGFloat h,s,v,a; 362 | if (![self hue:&h saturation:&s brightness:&v alpha:&a]) return nil; 363 | 364 | NSMutableArray* colors = [NSMutableArray arrayWithCapacity:pairs * 2]; 365 | 366 | if (stepAngle < 0.0f) 367 | stepAngle *= -1.0f; 368 | 369 | for (int i = 1; i <= pairs; ++i) { 370 | CGFloat a = fmodf(stepAngle * i, 360.0f); 371 | 372 | CGFloat h1 = fmodf(h + a, 360.0f); 373 | CGFloat h2 = fmodf(h + 360.0f - a, 360.0f); 374 | 375 | [colors addObject:[UIColor colorWithHue:h1 saturation:s brightness:v alpha:a]]; 376 | [colors addObject:[UIColor colorWithHue:h2 saturation:s brightness:v alpha:a]]; 377 | } 378 | 379 | return [[colors copy] autorelease]; 380 | } 381 | 382 | #pragma mark String utilities 383 | 384 | - (NSString *)stringFromColor { 385 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -stringFromColor"); 386 | NSString *result; 387 | switch (self.colorSpaceModel) { 388 | case kCGColorSpaceModelRGB: 389 | result = [NSString stringWithFormat:@"{%0.3f, %0.3f, %0.3f, %0.3f}", self.red, self.green, self.blue, self.alpha]; 390 | break; 391 | case kCGColorSpaceModelMonochrome: 392 | result = [NSString stringWithFormat:@"{%0.3f, %0.3f}", self.white, self.alpha]; 393 | break; 394 | default: 395 | result = nil; 396 | } 397 | return result; 398 | } 399 | 400 | - (NSString *)hexStringFromColor { 401 | return [NSString stringWithFormat:@"%0.6X", self.rgbHex]; 402 | } 403 | 404 | - (NSString *)closestColorName { 405 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use closestColorName"); 406 | 407 | int targetHex = self.rgbHex; 408 | int rInt = (targetHex >> 16) & 0x0ff; 409 | int gInt = (targetHex >> 8) & 0x0ff; 410 | int bInt = (targetHex >> 0) & 0x0ff; 411 | 412 | float bestScore = MAXFLOAT; 413 | const char* bestPos = nil; 414 | 415 | // Walk the colorNameDB looking for the name with closest match 416 | for (const char* p = colorNameDB; (p = strchr(p, '#')); ++p) { 417 | int r,g,b; 418 | if (sscanf(p+1, "%2x%2x%2x", &r, &g, &b) == 3) { 419 | // Calculate difference between this color and the target color 420 | int rDiff = abs(rInt - r); 421 | int gDiff = abs(gInt - g); 422 | int bDiff = abs(bInt - b); 423 | float score = logf(rDiff+1) + logf(gDiff+1) + logf(bDiff+1); 424 | 425 | // Track the best score/name seen 426 | if (score < bestScore) { 427 | bestScore = score; 428 | bestPos = p; 429 | } 430 | } 431 | } 432 | 433 | // bestPos now points to the # following the best name seen 434 | // Backup to the start of the name and return it 435 | const char* name; 436 | for (name = bestPos-1; *name != ','; --name) 437 | ; 438 | ++name; 439 | NSString *result = [[[NSString alloc] initWithBytes:name length:bestPos - name encoding:NSUTF8StringEncoding] autorelease]; 440 | 441 | return result; 442 | } 443 | 444 | + (UIColor *)colorWithString:(NSString *)stringToConvert { 445 | NSScanner *scanner = [NSScanner scannerWithString:stringToConvert]; 446 | if (![scanner scanString:@"{" intoString:NULL]) return nil; 447 | const NSUInteger kMaxComponents = 4; 448 | CGFloat c[kMaxComponents]; 449 | NSUInteger i = 0; 450 | if (![scanner scanFloat:&c[i++]]) return nil; 451 | while (1) { 452 | if ([scanner scanString:@"}" intoString:NULL]) break; 453 | if (i >= kMaxComponents) return nil; 454 | if ([scanner scanString:@"," intoString:NULL]) { 455 | if (![scanner scanFloat:&c[i++]]) return nil; 456 | } else { 457 | // either we're at the end of there's an unexpected character here 458 | // both cases are error conditions 459 | return nil; 460 | } 461 | } 462 | if (![scanner isAtEnd]) return nil; 463 | UIColor *color; 464 | switch (i) { 465 | case 2: // monochrome 466 | color = [UIColor colorWithWhite:c[0] alpha:c[1]]; 467 | break; 468 | case 4: // RGB 469 | color = [UIColor colorWithRed:c[0] green:c[1] blue:c[2] alpha:c[3]]; 470 | break; 471 | default: 472 | color = nil; 473 | } 474 | return color; 475 | } 476 | 477 | #pragma mark Class methods 478 | 479 | + (UIColor *)randomColor { 480 | return [UIColor colorWithRed:random() / (CGFloat)RAND_MAX 481 | green:random() / (CGFloat)RAND_MAX 482 | blue:random() / (CGFloat)RAND_MAX 483 | alpha:1.0f]; 484 | } 485 | 486 | + (UIColor *)colorWithRGBHex:(UInt32)hex { 487 | int r = (hex >> 16) & 0xFF; 488 | int g = (hex >> 8) & 0xFF; 489 | int b = (hex) & 0xFF; 490 | 491 | return [UIColor colorWithRed:r / 255.0f 492 | green:g / 255.0f 493 | blue:b / 255.0f 494 | alpha:1.0f]; 495 | } 496 | 497 | // Returns a UIColor by scanning the string for a hex number and passing that to +[UIColor colorWithRGBHex:] 498 | // Skips any leading whitespace and ignores any trailing characters 499 | + (UIColor *)colorWithHexString:(NSString *)stringToConvert { 500 | NSScanner *scanner = [NSScanner scannerWithString:stringToConvert]; 501 | unsigned hexNum; 502 | if (![scanner scanHexInt:&hexNum]) return nil; 503 | return [UIColor colorWithRGBHex:hexNum]; 504 | } 505 | 506 | // Lookup a color using css 3/svg color name 507 | + (UIColor *)colorWithName:(NSString *)cssColorName { 508 | return [[UIColor namedColors] objectForKey:cssColorName]; 509 | } 510 | 511 | // Return complete mapping of css3/svg color names --> colors 512 | + (NSDictionary *)namedColors { 513 | [colorNameCacheLock lock]; 514 | if (colorNameCache == nil) [UIColor populateColorNameCache]; 515 | [colorNameCacheLock unlock]; 516 | return colorNameCache; 517 | } 518 | 519 | + (UIColor *)colorWithHue:(CGFloat)hue saturation:(CGFloat)saturation brightness:(CGFloat)brightness alpha:(CGFloat)alpha { 520 | // Convert hsb to rgb 521 | CGFloat r,g,b; 522 | [self hue:hue saturation:saturation brightness:brightness toRed:&r green:&g blue:&b]; 523 | 524 | // Create a color with rgb 525 | return [self colorWithRed:r green:g blue:b alpha:alpha]; 526 | } 527 | 528 | 529 | #pragma mark Color Space Conversions 530 | 531 | + (void)hue:(CGFloat)h saturation:(CGFloat)s brightness:(CGFloat)v toRed:(CGFloat *)pR green:(CGFloat *)pG blue:(CGFloat *)pB { 532 | CGFloat r,g,b; 533 | 534 | // From Foley and Van Dam 535 | 536 | if (s == 0.0f) { 537 | // Achromatic color: there is no hue 538 | r = g = b = v; 539 | } else { 540 | // Chromatic color: there is a hue 541 | if (h == 360.0f) h = 0.0f; 542 | h /= 60.0f; // h is now in [0, 6) 543 | 544 | int i = floorf(h); // largest integer <= h 545 | CGFloat f = h - i; // fractional part of h 546 | CGFloat p = v * (1 - s); 547 | CGFloat q = v * (1 - (s * f)); 548 | CGFloat t = v * (1 - (s * (1 - f))); 549 | 550 | switch (i) { 551 | case 0: r = v; g = t; b = p; break; 552 | case 1: r = q; g = v; b = p; break; 553 | case 2: r = p; g = v; b = t; break; 554 | case 3: r = p; g = q; b = v; break; 555 | case 4: r = t; g = p; b = v; break; 556 | case 5: r = v; g = p; b = q; break; 557 | } 558 | } 559 | 560 | if (pR) *pR = r; 561 | if (pG) *pG = g; 562 | if (pB) *pB = b; 563 | } 564 | 565 | 566 | + (void)red:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b toHue:(CGFloat *)pH saturation:(CGFloat *)pS brightness:(CGFloat *)pV { 567 | CGFloat h,s,v; 568 | 569 | // From Foley and Van Dam 570 | 571 | CGFloat max = MAX(r, MAX(g, b)); 572 | CGFloat min = MIN(r, MIN(g, b)); 573 | 574 | // Brightness 575 | v = max; 576 | 577 | // Saturation 578 | s = (max != 0.0f) ? ((max - min) / max) : 0.0f; 579 | 580 | if (s == 0.0f) { 581 | // No saturation, so undefined hue 582 | h = 0.0f; 583 | } else { 584 | // Determine hue 585 | CGFloat rc = (max - r) / (max - min); // Distance of color from red 586 | CGFloat gc = (max - g) / (max - min); // Distance of color from green 587 | CGFloat bc = (max - b) / (max - min); // Distance of color from blue 588 | 589 | if (r == max) h = bc - gc; // resulting color between yellow and magenta 590 | else if (g == max) h = 2 + rc - bc; // resulting color between cyan and yellow 591 | else /* if (b == max) */ h = 4 + gc - rc; // resulting color between magenta and cyan 592 | 593 | h *= 60.0f; // Convert to degrees 594 | if (h < 0.0f) h += 360.0f; // Make non-negative 595 | } 596 | 597 | if (pH) *pH = h; 598 | if (pS) *pS = s; 599 | if (pV) *pV = v; 600 | } 601 | 602 | 603 | #pragma mark UIColor_Expanded initialization 604 | 605 | + (void)load { 606 | colorNameCacheLock = [[NSLock alloc] init]; 607 | } 608 | 609 | @end 610 | 611 | #pragma mark - 612 | 613 | #if SUPPORTS_UNDOCUMENTED_API 614 | @implementation UIColor (UIColor_Undocumented_Expanded) 615 | - (NSString *)fetchStyleString { 616 | return [self styleString]; 617 | } 618 | 619 | // Convert a color into RGB Color space, courtesy of Poltras 620 | // via http://ofcodeandmen.poltras.com/2009/01/22/convert-a-cgcolorref-to-another-cgcolorspaceref/ 621 | // 622 | - (UIColor *)rgbColor { 623 | // Call to undocumented method "styleString". 624 | NSString *style = [self styleString]; 625 | NSScanner *scanner = [NSScanner scannerWithString:style]; 626 | CGFloat red, green, blue; 627 | if (![scanner scanString:@"rgb(" intoString:NULL]) return nil; 628 | if (![scanner scanFloat:&red]) return nil; 629 | if (![scanner scanString:@"," intoString:NULL]) return nil; 630 | if (![scanner scanFloat:&green]) return nil; 631 | if (![scanner scanString:@"," intoString:NULL]) return nil; 632 | if (![scanner scanFloat:&blue]) return nil; 633 | if (![scanner scanString:@")" intoString:NULL]) return nil; 634 | if (![scanner isAtEnd]) return nil; 635 | 636 | return [UIColor colorWithRed:red green:green blue:blue alpha:self.alpha]; 637 | } 638 | @end 639 | #endif // SUPPORTS_UNDOCUMENTED_API 640 | 641 | @implementation UIColor (UIColor_Expanded_Support) 642 | /* 643 | * Database of color names and hex rgb values, derived 644 | * from the css 3 color spec: 645 | * http://www.w3.org/TR/css3-color/ 646 | * 647 | * We think this is a very compact way of storing 648 | * this information, and relatively cheap to lookup. 649 | * 650 | * Note that we search for color names starting with ',' 651 | * and terminated by '#', so that we don't get false matches. 652 | * For this reason, the database begins with ','. 653 | */ 654 | static const char *colorNameDB = "," 655 | "aliceblue#f0f8ff,antiquewhite#faebd7,aqua#00ffff,aquamarine#7fffd4,azure#f0ffff," 656 | "beige#f5f5dc,bisque#ffe4c4,black#000000,blanchedalmond#ffebcd,blue#0000ff," 657 | "blueviolet#8a2be2,brown#a52a2a,burlywood#deb887,cadetblue#5f9ea0,chartreuse#7fff00," 658 | "chocolate#d2691e,coral#ff7f50,cornflowerblue#6495ed,cornsilk#fff8dc,crimson#dc143c," 659 | "cyan#00ffff,darkblue#00008b,darkcyan#008b8b,darkgoldenrod#b8860b,darkgray#a9a9a9," 660 | "darkgreen#006400,darkgrey#a9a9a9,darkkhaki#bdb76b,darkmagenta#8b008b," 661 | "darkolivegreen#556b2f,darkorange#ff8c00,darkorchid#9932cc,darkred#8b0000," 662 | "darksalmon#e9967a,darkseagreen#8fbc8f,darkslateblue#483d8b,darkslategray#2f4f4f," 663 | "darkslategrey#2f4f4f,darkturquoise#00ced1,darkviolet#9400d3,deeppink#ff1493," 664 | "deepskyblue#00bfff,dimgray#696969,dimgrey#696969,dodgerblue#1e90ff," 665 | "firebrick#b22222,floralwhite#fffaf0,forestgreen#228b22,fuchsia#ff00ff," 666 | "gainsboro#dcdcdc,ghostwhite#f8f8ff,gold#ffd700,goldenrod#daa520,gray#808080," 667 | "green#008000,greenyellow#adff2f,grey#808080,honeydew#f0fff0,hotpink#ff69b4," 668 | "indianred#cd5c5c,indigo#4b0082,ivory#fffff0,khaki#f0e68c,lavender#e6e6fa," 669 | "lavenderblush#fff0f5,lawngreen#7cfc00,lemonchiffon#fffacd,lightblue#add8e6," 670 | "lightcoral#f08080,lightcyan#e0ffff,lightgoldenrodyellow#fafad2,lightgray#d3d3d3," 671 | "lightgreen#90ee90,lightgrey#d3d3d3,lightpink#ffb6c1,lightsalmon#ffa07a," 672 | "lightseagreen#20b2aa,lightskyblue#87cefa,lightslategray#778899," 673 | "lightslategrey#778899,lightsteelblue#b0c4de,lightyellow#ffffe0,lime#00ff00," 674 | "limegreen#32cd32,linen#faf0e6,magenta#ff00ff,maroon#800000,mediumaquamarine#66cdaa," 675 | "mediumblue#0000cd,mediumorchid#ba55d3,mediumpurple#9370db,mediumseagreen#3cb371," 676 | "mediumslateblue#7b68ee,mediumspringgreen#00fa9a,mediumturquoise#48d1cc," 677 | "mediumvioletred#c71585,midnightblue#191970,mintcream#f5fffa,mistyrose#ffe4e1," 678 | "moccasin#ffe4b5,navajowhite#ffdead,navy#000080,oldlace#fdf5e6,olive#808000," 679 | "olivedrab#6b8e23,orange#ffa500,orangered#ff4500,orchid#da70d6,palegoldenrod#eee8aa," 680 | "palegreen#98fb98,paleturquoise#afeeee,palevioletred#db7093,papayawhip#ffefd5," 681 | "peachpuff#ffdab9,peru#cd853f,pink#ffc0cb,plum#dda0dd,powderblue#b0e0e6," 682 | "purple#800080,red#ff0000,rosybrown#bc8f8f,royalblue#4169e1,saddlebrown#8b4513," 683 | "salmon#fa8072,sandybrown#f4a460,seagreen#2e8b57,seashell#fff5ee,sienna#a0522d," 684 | "silver#c0c0c0,skyblue#87ceeb,slateblue#6a5acd,slategray#708090,slategrey#708090," 685 | "snow#fffafa,springgreen#00ff7f,steelblue#4682b4,tan#d2b48c,teal#008080," 686 | "thistle#d8bfd8,tomato#ff6347,turquoise#40e0d0,violet#ee82ee,wheat#f5deb3," 687 | "white#ffffff,whitesmoke#f5f5f5,yellow#ffff00,yellowgreen#9acd32"; 688 | 689 | + (void)populateColorNameCache { 690 | NSAssert(colorNameCache == nil, @"+pouplateColorNameCache was called when colorNameCache was not nil"); 691 | NSMutableDictionary *cache = [NSMutableDictionary dictionary]; 692 | for (const char* entry = colorNameDB; entry = strchr(entry, ','); ) { 693 | 694 | // Step forward to the start of the name 695 | ++entry; 696 | 697 | // Find the following hash 698 | const char* h = strchr(entry, '#'); 699 | NSAssert(h, @"Malformed colorNameDB"); 700 | 701 | // Get the name 702 | NSString* name = [[NSString alloc] initWithBytes:entry length:h - entry encoding:NSUTF8StringEncoding]; 703 | 704 | // Get the color, and add to the dictionary 705 | int hex, increment; 706 | if (sscanf(++h, "%x%n", &hex, &increment) != 1) break; 707 | [cache setObject:[self colorWithRGBHex:hex] forKey:name]; 708 | 709 | // Cleanup and move to the next item 710 | [name release]; 711 | entry = h + increment; 712 | } 713 | colorNameCache = [cache copy]; 714 | } 715 | @end 716 | --------------------------------------------------------------------------------