External Constants for UIColor, UIFont, etc - objective-c

I have a constants.m file that is a centralized collection of many program constants. To set a color, I do this:
#implementation UIColor (UIColor_Constants)
+(UIColor *) defaultResultTableBackgroundColor{
//return [[UIColor colorWithRed:0.6f green:0.004f blue:0.0f alpha:1.0f] retain];
return [[UIColor colorWithRed:0.1f green:0.004f blue:0.3f alpha:0.3f] retain];
}
+(UIColor *) defaultResultHeaderBackgroundColor{
return [[UIColor clearColor] retain];
}
#end
and in the constants.h I have
#interface UIColor (UIColor_Constants)
+(UIColor *) defaultResultTableBackgroundColor;
+(UIColor *) defaultResultHeaderBackgroundColor;
#end
and then just use [UIColor defaultResultTableBackgroundColor] where I want to refer to this constant.
I would like to have some other UIColor and UIFont constants, and, while this works, it seems to be more complicated than it needs to be. Is there an easier way to do this?

I use a constants file for the same purpose. Rather than setting up a whole interface and implementation file, I create the constants file as just the header (.h) file. Then, I define the colors I want to use, such as:
#define globalColor [UIColor colorWithRed:0.1f green:0.004f blue:0.3f alpha:0.3f]
Then, any time you use globalColor it's just like typing in the defined code.

I actually like this way too. One question: why do you retain the uicolor?
This is very dangerous. It's very likely to make a mistake and to create a memory leak.

Here's a great read that explains constants in Objective C:
What is the best way to create constants in Objective-C
Short answer: You're handling this the best way possible.
Macros are not recommended. As some have mentioned, you could #define a macro to handle your colors. That's essentially telling the preprocessor to run find-and-replace on your code. There's a number of downsides to this approach, including scope and type. Apple explicitly recommends against these types of 'constants": https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html
It's also possible to create a constant:
(in your header, file scope)
extern UIColor * const COLOR_LIGHT_BLUE;
(in your implementation, file scope)
UIColor* const COLOR_LIGHT_BLUE = [[UIColor alloc] initWithRed:21.0f/255 green:180.0f/255 blue:1 alpha:1];
You could, of course, #import this header in your prefix header to save even more typing. Ultimately, though, it's not much of an improvement over what you're already doing.

You create a constant header file which contains all your colors and/or fonts like that:
// Define a convenient macro for example
#define HEXCOLOR(c) [UIColor colorWithRed:((c>>16)&0xFF)/255.0 \
green:((c>>8)&0xFF)/255.0 \
blue:c&0xFF/255.0 \
alpha:0xFF/255.0]
// Then define your constants
#define DefaultResultTableBackgroundColor HEXCOLOR(0x151515)
// etc.

Answer is very simple. It can be used for NSArray and NSDictionary etc.
//Font
static UIFont *titleFont() {
static UIFont *font = nil;
if (!font) {
font = [UIFont systemFontOfSize:25 weight:UIFontWeightHeavy];
}
return font;
}
//Color
static UIColor *prettyPurpleColor() {
static UIColor *color = nil;
if (!color) {
color = [[UIColor purpleColor] colorWithAlphaComponent:0.35];
}
return color;
}

Related

Calling Obj-C Methods

I want to call a method B using a different method A. The problem is that the aksed parameters of method B is not present in Method A.
 
here's what I've tried ..
-(void) methodA {
// some code
CGSize *size = [CGSizeMake(self.frame.size.width, self.frame.size.height)];
[self methodB:size];
}
-(void) methodB:(CGSize) size {
//some code
}
There is certainly a better way...
Thanks
I don't think your code will be compiled.
CGSize is not an object.
Refactor to:
CGSize size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self methodB:size];
You should only wrap [] around methods. CGSizeMake(self.frame.size.width, self.frame.size.height) is a function, not a method, so get rid of the square braces. You can tell it's a function because functions look like functionName(argument1, argument2, ...) whereas methods look like [object methodName:argument1 methondNameContinued:argument2].
Further, CGSizeMake returns a CGSize structure, not a pointer to a CGSize structure, so ditch the * in *size.
Then you'll be left with this:
CGSize size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self methodB:size];
Which is correct.

In Objective-c how can we have a variable whose scope is the whole class (but doesn't include subclasses)

I created tons of subclasses of UITableViewCell. Each of which is a subclass of an additional BGBaseTableViewCell class.
When asked for height, I think it'll be useful to tell users what's the typical size of the UITableViewCell.
One way to do it is to specify that twice.
One when I design the UITableViewCell in xib, and the second time when I hard coded the typical size.
This is a design flaw because then I will need to remember to change the hard coded typical size when I change the stuff in xib.
I don't want that. I want to design my UITableViewCell in xib and when the class is initialized I want to store the default height.
the default height need to be different for all subclasses. However, it need to be the same for all instances for that subclass.
If I use simple ivar, then I will have to store that one default size for all instance.
If I use static variable, then the static variable in BGBaseTableViewCell is used by all it's subclasses.
So what should I do?
This is what I am doing now:
static CGFloat defaultHeightSet = 0; //this one is used by all subclasses. I want each subclass have it's own defaultHeightSet, but I want to specify that fact once.
+(void)initialize
{
BGBaseTableViewCell * typical = [[self alloc]init];
defaultHeightSet = typical.bounds.size.height;
}
+(CGFloat) defaultHeight
{
return defaultHeightSet;
}
Visible in whole class but not for subclasses? You can try this code for your .m file:
#interface ClassYouNeed ()
static VariableClass* variableVisibleInClassButNotInSubclass;
#end
Just make a "hidden" category on your class near your implementation, it should work.
Why didn't I think of it.
Make the static variable a dictionary and set the class names as the keys :)
Ladies and gentlemen, children of all ages. Sharen Eayrs production proudly presents, statically protected variables:
static NSMutableDictionary * defaultHeightDictionary= nil;
static NSMutableDictionary * defaultBoundsDictionary =nil;
//static CGFloat defaultHeightSet = 0;
+(void)initialize
{
BGBaseTableViewCell * typical = [[self alloc]init];
if (defaultHeightDictionary==nil) {
defaultHeightDictionary = [NSMutableDictionary dictionary];
defaultBoundsDictionary = [NSMutableDictionary dictionary];
}
[defaultHeightDictionary setValue:#(typical.bounds.size.height) forKey:NSStringFromClass([self class])];
CGRect bounds = typical.bounds;
NSValue * boundsValue = [NSValue valueWithCGRect:bounds];
[defaultHeightDictionary setValue:boundsValue forKey:NSStringFromClass([self class])];
}
+(CGFloat) defaultHeight
{
NSNumber * result = [defaultHeightDictionary valueForKey:NSStringFromClass([self class])];
return result.floatValue;
}
+(CGRect) defaultBounds
{
NSValue * result = [defaultBoundsDictionary valueForKey:NSStringFromClass([self class])];
return [result CGRectValue];
}

Using #define to create styles

I am wondering if I can use a series of #DEFINE to create style options for my app.
For example,
#DEFINE style1: backGroundColor = [UIColor: colorNamed whiteColor];
txtColor = [UIColor blackColor]; #DEFINE style2....
My question is: What is the syntax for this statement?
While it is certainly possible to use DEFINE statements to create style options for your app, I'm wondering if the use of preprocessor directives makes sense for functionality such as styles, which are going to be collections to properties. If you use DEFINE statements to define your styles, it makes it difficult to ultimately provide style selection as a run-time option to your users.
Instead, I tend to think you will be better off creating a class hierarchy for this, and implementing it as a singleton. With a class hierarchy, you can define any general style behaviors in your root class, and then inherit from that to implement specific styles. Later on, you can expose the ability to select styles to your user if you want.
#define backgroundColor [UIColor whiteColor] then to use it you would say UIColor *txtColor = backgroundColor;.
Although you may only be able to use whiteColor as the definition instead of [UIColor whiteColor]. You would then call [UIColor backgroundColor]; instead of the above example.
I would not do this to generate stylings. Defining various settings outside of code would be a good idea but using defines is pretty binding and defeats the purpose of de-coupling the UI from code.
In a #define do not put a semicolon anywhere. When the preprocessor inserts the definition it will insert whatever semicolon is there exactly as it is; you do not want the preprocessor inserting semicolons. When writing the definition you probably don't know all the places you'll write it, therefore you shouldn't have a semicolon in it because you may write it inline an expression.
Another option is to use const instead.
In code I've written I have #defines for string literals #"literal string" and numbers. In other places I use the const declaration which looks like this:
//static type *const variableName = assignment;
static NSString *const kConstantString = #"Constant variable";
Constants don't use the preprocessor to fill in the information. If you access a define frequently and it uses some computation it might may be better suited to a constant declaration which is stored only once.
The other big reason I used const instead of #define is that define is not type-checked as it's handled by the preprocessor. Define basically turns off the compiler warnings and only gives you errors; strict warnings are extremely helpful and can save a lot of frustration.
Maybe you are looking for this syntax:
Setting a style
#define STYLE1
//#define STYLE2
and then anywhere in your code (class level or method level, header or implementation file).
#ifdef STYLE1
//code for the first style
UIColor* backgroundColor = [UIColor redColor];
#elif STYLE2
//code for the second style
UIColor* backgroundColor = [UIColor greenColor];
#else
//code for the third style
UIColor* backgroundColor = [UIColor clearColor];
#endif
More about C preprocessor: http://en.wikipedia.org/wiki/C_preprocessor

Objective-C forwardInvocation:

I often do something like:
CoolViewController *coolViewController = [[CoolViewController alloc] init];
[self.navigationController pushViewController:coolViewController animated:YES];
[coolViewController release];
How would I, in a category of UINavigationController, override forwardInvocation: so that I could just instead do:
[self.navigationController pushCoolViewControllerAnimated:YES];
Please include the relevant code in your answer, not just an explanation. Thank you!
Feel free to comment on whether this is good practice. I'm also asking this for educational purposes, but it seems to me that in this case, the simplification in code may outweight the unnoticeable (correct?) cost in processing time & memory usage. Also, I come from a Ruby background and love to use dynamic programming to simplify things, e.g., dynamic finders (e.g., find_by_name) in Rails.
Bonus points if you could implement pushCoolViewControllerAnimated:withBlock and invoke the block after initializing the view controller, allowing me to set certain instance variables on the view controller created.
UPDATE: I just remembered that ARC is coming soon. So this specific example may not be so helpful then, but still a great exercise/example that could be used in other cases, e.g., dynamic finders for Core Data & passing a block to configure the NSFetchRequest.
Use the dynamic method resolution mechanism described in the Objective-C Runtime Programming Guide, specifically, +[NSObject resolveInstanceMethod:]:
#implementation UINavigationController (FWD)
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *name = NSStringFromSelector(sel);
NSString *prefix = #"push";
NSString *suffix = #"Animated:";
if ([name hasPrefix:prefix] && [name hasSuffix:suffix]) {
NSRange classNameRange = {[prefix length],
[name length] - [prefix length] - [suffix length]}
NSString *className = [name substringWithRange:classNameRange];
Class cls = NSClassFromString(className);
if (cls) {
IMP imp = imp_implementationWithBlock(
^(id me, BOOL animated) {
id vc = [[cls alloc] init];
[me pushViewController:vc animated:animated];
[vc release];
});
class_addMethod(cls, sel, imp, "v#:c");
return YES;
}
}
return [super resolveInstanceMethod:sel];
}
#end
Of course, if UINavigationController already uses +resolveInstanceMethod:, you've now broken it. Doing this in a subclass of UINavigationController, or using method swizzling to enable invoking the original implementation, would solve that problem.
The version accepting a post-creation block is a straightforward extension (change the block parameters, change the type encoding, change the selector name pattern and how you extract the intended class name).

How to use a C array as instance variable?

For example I want to store this in an ivar:
CGFloat color[4] = {red, green, blue, 1.0f};
so would I put this in my header?
CGFloat color[];
How would I assign values to that guy later? I mean I can't change it, right?
Instance variables are zeroed out on allocation so you can't use initialisers with them.
You need something like this:
// MyObject.h
#interface MyObject
{
CGFloat color[4];
}
#end
// MyObject.m
#implementation MyObject
-(id) init
{
self = [super init];
if (self != nil)
{
color[0] = red;
color[1] = green;
color[2] = blue;
color[3] = alpha;
}
return self;
}
You'd need to put the size in so that enough space is reserved.
CGFloat color[4];
Or use a pointer to the array, but that's more work and hardly superior for representing something as well-known as a color.
You are better off using a NSColor object if you can.
However, to your original question one of my first questions is where do you want to create this array. When you say put it in a header do you mean as a member of a class or as a global array, you certainly can do both however there are some serious gotchas with putting globals in headers. If you need that follow up and I can explain it better.
If it is in a class then you can just declare it like any other member field. If you say
CGFloat color[4];
then the space for the array is allocated in your object itself. You can also just use a
CGFloat *color;
or its moral equivalent to refer to an array that is stored outside of the object. You do need to manage that storage appropriately however.
This matters in the case you hinted at where you use a constant array object and cannot later change it. That can happen but is rare, since it cannot happen with the first approach, you don't see it in the wild very often.
There is a whole dissertation on the corner cases in here, I am sure it is not helping to go into it. Just use CGFloat color[4] in your object and it won't matter, by the time you see things they will be mutable and you can just use them the way you expect.