30

I have a iPhone application with a few custom-defined colors for my theme. Since these colors will be fixed for my UI, I would like to define the colors in a class to be included (Constants.h and Constants.m). How do I do that? (Simply defining them does not work because UIColors are mutable, and would cause errors - Initalizer not constant).

/* Constants.h */
extern UIColor *test;

/* Constants.m */
UIColor *test = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];

Thanks!

futureelite7
  • 11,227
  • 9
  • 51
  • 85

7 Answers7

48

A UIColor is not mutable. I usually do this with colors, fonts and images. You could easily modify it to use singletons or have a static initializer.

@interface UIColor (MyProject)

+(UIColor *) colorForSomePurpose;

@end

@implementation UIColor (MyProject)

+(UIColor *) colorForSomePurpose { return [UIColor colorWithRed:0.6 green:0.8 blue:1.0 alpha:1.0]; }

@end
drawnonward
  • 52,256
  • 15
  • 103
  • 110
  • 1
    -1 for the first sentence - the Initializer is what is not constant, not the instance. For all intents and purposes, UIColor could (or could tomorrow) contain internal state variables which are unexposed to the user, making them seem to us constant, however there is nothing in Obj-C forcing this. Therefore the compiler cannot use a static initializer, like it would like to, because [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0] is a method call, and thus the results of this cannot be put in the text segment, which is created at compile time. – Jared Pochtar May 13 '10 at 04:28
  • 6
    Mutable means that you can change the instance. Immutable means you cannot change the instance. Constant means that the instance cannot be changed. A UIColor is not mutable and also not constant. Only CFString/NSString has truly constant instances because they are handled specially as part of the language by the compiler. – drawnonward May 13 '10 at 15:32
  • 1
    Can you please explain what is (MyProject). I'm trying to use it in NSObject based class and the syntax is @interface Common : NSObject – Naveed Abbas Oct 09 '17 at 06:25
  • @ToughGuy - it's an extension. The "MyProject" message is somewhat irrelevant but necessary (I don't know why, but you I used `(extension)` instead) so you can keep it there. Use like so: `[UIColor colorForSomePurpose]` just like you would red `[UIColor red]` and etc. `[self.view setBackgroundColor: [UIColor colorForSomePurpose] ];` – App Dev Guy Oct 17 '17 at 08:21
32

For simplicity I did this:

/* Constants.h */
#define myColor [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0]

Don't forget to leave out the ';' so you can use it as a normal expression.

I'm not sure if there's anything technically wrong with this approach, but it works fine, and avoids the compile-time constant initializer error - this code is effectively stuck in place anywhere you put 'myColor', so it doesn't ever get compiled until you actually use it.

Fateh Khalsa
  • 1,216
  • 1
  • 15
  • 19
  • interesting approach! I'd be interested why the ';' can be left out? How is this called? – brainray Dec 29 '13 at 18:57
  • 2
    It's a preprocessor macro. Effectively 'myColor' is replaced wherever it appears in your code with the full '[UIColor colorWith... ' line of code, before it ever compiles. You leave out the ';' in the macro so you can use the code like this: `UIColor *color = myColor;` rather than `UIColor *color = myColor`, which doesn't look like a normal line of code (it's more natural to place a semicolon at the end of any line of code). It can be left out because preprocessor macros are evaluated as they are when they're used, so as long as you place a semicolon in your actual code, you're fine. – Fateh Khalsa Dec 30 '13 at 20:26
  • 2
    But doesn't this create a new instance of this color every time you use this macro? Consider having it in a for loop with 1000 iterations and assigning it as a background color to a view. This is not very efficient is it? – Majster Jan 05 '14 at 17:20
  • That is true, but where are you ever going to use this 1000 times? Also, if you were to create the colors normally, it would be the same case. This just allows you the convenience of defining the color once so it can be changed easily. The other methods mentioned in answers here will of course avoid creating those multiple instances, but they are more work to implement and less elegant in my opinion. The actual overhead incurred from the number of times you are likely to use this in a view should be negligible. Just my two cents FWIW though. – Fateh Khalsa Mar 20 '14 at 18:06
14

Another option

in your .h you can do

extern UIColor *  const COLOR_LIGHT_BLUE;

in your .mm you can do

UIColor* const COLOR_LIGHT_BLUE = [[UIColor alloc] initWithRed:21.0f/255 green:180.0f/255  blue:1 alpha:1];//;#15B4FF
yeahdixon
  • 6,131
  • 1
  • 36
  • 42
  • 3
    What is this shifty C++ dynamic constants? ;) +1 – Greg Apr 16 '12 at 13:27
  • I'm interested in why this possible too. – Taketo Sano Jul 09 '13 at 03:49
  • It's possible, because `T* const` is a constant pointer to a regular object (you cannot modify where such a pointer points to), as opposed to `const T*`, which is a pointer to a constant object (you cannot modify the object through such a pointer). – Daniel S. Aug 02 '13 at 04:31
5

If you're looking for a quick and dirty one without extensions go with clang:

#define kGreenColor colorWithRed:(0/255.0) green:(213/255.0) blue:(90/255.0) alpha:1.0

- (void)doSomething
{
   _label.textColor = [UIColor kGreenColor];

}
Albert Renshaw
  • 15,644
  • 17
  • 92
  • 173
AmitP
  • 4,985
  • 4
  • 31
  • 27
0

Here's another way:

Header:

#if !defined(COLORS_EXTERN)
    #define COLORS_EXTERN extern
#endif

COLORS_EXTERN UIColor *aGlobalColor;

Implementation:

#define COLORS_EXTERN
#import "GlobalColors.h"


@interface GlobalColors : NSObject
@end

@implementation GlobalColors

+ (void)load
{
    aGlobalColor = [UIColor colorWithRed:0.2 green:0.3 blue:0.4 alpha:1];
}

@end

It's a bit of a hack, but you don't need to redefine the color in the implementation and you can access colors without a method call.

goetz
  • 906
  • 6
  • 14
0

Often people put global constants into singleton objects - or as drawnonward noted, you can make them accessible via a class method of some class.

Kendall Helmstetter Gelner
  • 73,251
  • 26
  • 123
  • 148
-2

Use the AppController to make the colors accessible globally, rather than a static variable. That way it makes sense from an architecture standpoint, and also if you wanted to hypothetically change color schemes, even while running, this would just be a method or two on the AppController

Jared Pochtar
  • 4,727
  • 2
  • 23
  • 38
  • AppController - I guess you are referring to the UIApplication delegate? – Till Apr 09 '12 at 17:19
  • Changing colors at runtime would be much bigger task in most applications. Also good way to pollute the app delegate with code that doesn't have anything to do with it. – Sulthan Apr 09 '12 at 18:05