iObjectiveSee

Cocoa and iOS Development

Archive for the ‘Uncategorized’ Category

building (array methods using) blocks

leave a comment

My friend Miguel and I recently learned about a few common Array methods in ruby which are invoked using blocks. Among the many appealing are some choice methods: #uniq, #each, #collect, #flatten, and #reject. We decided to implement a few of these in objective-c.

Say your original code resembles this:


NSArray *cities = [NSArray arrayWithObjects:@"New York", @"San Francisco", @"Seatle", @"Paris", @"Verbier", @"Jay Peak", nil];

NSString *favorite = @"Verbier";

 


- (BOOL)isAwesome {

BOOL isAws = NO;

for (NSString *str in cities) {

if ([str isEqualToString:favorite]) {

isAws = YES;

}

}

return isAws;

}

You can make it a lot more readable by writing something like this instead:


- (BOOL)isAwesome {

id favCity = [cities detect:^BOOL(NSString *str) {

return [str isEqualToString:favorite];

}];

return favCity ? YES:NO;

}

 

Grab the full implementation from github


- (void)each:(void (^)(id))block;

- (id)detect:(BOOL (^)(id))block;

- (NSArray *)collect:(id (^)(id obj))block;

- (NSArray *)reject:(BOOL (^)(id obj))block;

 

Written by Sarah

March 27th, 2012 at 12:56 am

Posted in Uncategorized

Essential UIView Additions

leave a comment

Where you have UIView *aView, you can now get (and set!) frame properties simply by saying “aView.left” or “aView.width”. There’s nothing new here, we simply ported some nice extensions found in Joe Hewitt’s work.

 

Header info:


@property (nonatomic) CGFloat left;

@property (nonatomic) CGFloat top;

@property (nonatomic) CGFloat right;

@property (nonatomic) CGFloat bottom;

@property (nonatomic) CGFloat width;

@property (nonatomic) CGFloat height;

 

…and the implementation:


- (CGFloat)left {

return self.frame.origin.x;

}

- (void)setLeft:(CGFloat)x {

CGRect frame = self.frame;

frame.origin.x = x;

self.frame = frame;

}

- (CGFloat)top {

return self.frame.origin.y;

}

- (void)setTop:(CGFloat)y {

CGRect frame = self.frame;

frame.origin.y = y;

self.frame = frame;

}

- (CGFloat)right {

return self.frame.origin.x + self.frame.size.width;

}

- (void)setRight:(CGFloat)right {

CGRect frame = self.frame;

frame.origin.x = right - frame.size.width;

self.frame = frame;

}

- (CGFloat)bottom {

return self.frame.origin.y + self.frame.size.height;

}

- (void)setBottom:(CGFloat)bottom {

CGRect frame = self.frame;

frame.origin.y = bottom - frame.size.height;

self.frame = frame;

}

- (CGFloat)width {

return self.frame.size.width;

}

- (void)setWidth:(CGFloat)width {

CGRect frame = self.frame;

frame.size.width = width;

self.frame = frame;

}

- (CGFloat)height {

return self.frame.size.height;

}

- (void)setHeight:(CGFloat)height {

CGRect frame = self.frame;

frame.size.height = height;

self.frame = frame;

}

 

 

Written by Sarah

March 11th, 2012 at 5:23 pm

Posted in Uncategorized

github.com/iObjectiveSee

leave a comment

Good news, folks.

We’ve setup a GitHub repository containing all of the code posted thus far. And more good news- we’ll continue to keep this up to date with future posts.

So, clone it, fork it…do whatever you like with the code!

 

Written by Sarah

February 15th, 2012 at 1:11 am

Posted in Uncategorized

Tagged with

CGSizeGreaterThanSize

leave a comment

Earlier this week I noticed that the CGGeometry class does not contain any convenience methods (besides CGSizeEqualToSize) for comparing one CGSize to another.  What I was looking for was a method to tell me if one size was greater than another. Joe brought up an interesting point- what exactly does it mean for one size to be greater than another? Having a larger width? Larger height? Both larger height and width? In my use case, a size should be considered greater than another size if either its width or height are larger, however I understand how this might differ in other applications (in fact, I vacillated between requiring both width and height to be larger). My solution was to create a category for CGGeometry and define an inline function:


CG_INLINE bool
__CGSizeGreaterThanSize(CGSize size1, CGSize size2) {
return size1.width > size2.width || size1.height > size2.height;
}
#define CGSizeGreaterThanSize __CGSizeGreaterThanSize

A lot of neat things are happening here so lets take a closer look. CG_INLINE is actually a macro that is telling the compiler that what follows should  be marked as an inline function. This saves us from having to write a few extra lines of code to determine the correct syntax for our compiler. Take a look at the Adam Wright’s response to this StackOverflow post for different inline function syntax examples.

What follows after CG_INLINE is the function return type (bool) and then the function definition. Our function name (__CGSizeGreaterThanSize) takes two parameters, size1 and size2 and returns YES if either size1′s width or height is greater than size2′s and NO if not.

#define is a very common preprocessor directive that allows you to define constants, macros, and in our case, inline functions. What we are doing here is saying future calls to CGSizeGreaterThanSize should invoke the function __CGSizeGreaterThanSize. Note that __CGSizeGreaterThanSize isn’t significant, just intuitive. We could have used any other name in its place.

This simple method helps us maintain clarity within our code. When CGSizeGreaterThanSize(aSize, anotherSize) is called, the code in our function body replaces the call to the function. This is significant because it means that we don’t transfer the control from the program to the function. Instead, we maintain within the program and CGSizeGreaterThanSize is not placed on our stack. Cool!

Written by Sarah

February 11th, 2012 at 6:38 pm

Random UIColor

4 comments

Just a quick tip on this Friday afternoon. I’ve been getting a fair amount of mileage out of this UIColor addition. It’s particularly useful when doing drawing in loops; randomizing the color every iteration can help make it clear what’s happening when and where. Enjoy!

+ (UIColor *)randomColor {
    return [self colorWithRed:((float)rand() / RAND_MAX) 
                        green:((float)rand() / RAND_MAX) 
                         blue:((float)rand() / RAND_MAX) 
                        alpha:1.0f];
}

Written by Joe

February 10th, 2012 at 8:30 pm

Posted in Uncategorized

Tagged with , ,

Preprocessor Macros

leave a comment

It’s useful to create symbols for values that are used throughout your project instead of hard-coding the same information in multiple places. Enter preprocessor macros.

There are two types of macros: object-like and function-like. Object-like macros are typically used to give symbolic names to numeric constants. They don’t take parameters and are conventionally defined in uppercase letters. Function-like macros, as their name suggests, take parameters in parenthesis (where there is no space between the parenthesis and the identifier) and are replacedby so-called “replacement tokens”.

Let’s take a look at two rudimentary examples:

1. #define VERSION 1.0

This creates an object-like macro which defines a symbol with identifier “VERSION” and replacement token “1.0″

2. #define SUM(a, b, c) a + b + c

This is an example function-like macro with identifier “SUM”, parameters “(a, b, c)” and replacement tokens “a + b + c”. Simple enough, right?

Joe and I have found it incredibly useful to use this nifty macro when printing to the console. It provides a bit more information than your basic “NSLog”. For a great discussion about its evolution, checkout Nick Dalton’s blog post.

 #ifdef DEBUG
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define DLog(...)
#endif 

We place this macro directly into our .pch file so that it can be called from any class without requiring an #import. In this case, it is obviously a good decision to make this globally accessible to all classes however, not all of our macros live in the .pch file. For an interesting post on .pch files, read this Cocoa Is My Girlfriend post. Here, Marcus uses the “DLog” macro as well as an additional “ALog” macro (when debugging) for throwing assertions. Here are a couple of our favorite object-like macros:


#define kMainWindow [[[UIApplication sharedApplication] delegate] window]

#define kMacDefaultDisabledAlpha 0.439216f

kMacDefaultDisabledAlpha is great for setting any UIView object to appear disabled with exactly the same alpha that Apple uses.

It is really nice to use RGB_COLOR and RGB_ALPHA_COLOR to avoid simple division when your designer gives you RGB values for in specs. I’ve also seen this functionality achieved via UIColor extensions.

#define RGB_COLOR(r, g, b) [UIColor colorWithRed:(r / 255.0f) green:(g / 255.0f) blue:(b / 255.0f) alpha:1.0f]

#define RGB_ALPHA_COLOR(r, g, b, a) [UIColor colorWithRed:(r / 255.0f) green:(g / 255.0f) blue:(b / 255.0f) alpha:(a < 1.0 ? a : (a / 255.0f))]

If you are going to be working with the documents directory or the bundle path checkout the next three macros:

#define DOCUMENT_PATH(inPath) [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:inPath]

#define BUNDLE_PATH(inPath) [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:inPath]

#define TEMPORARY_PATH(inPath) [NSTemporaryDirectory() stringByAppendingPathComponent:inPath]

IS_EMPTY_STRING can take anything as a parameter (yes, even an NSNull) and it will assert whether or not you’ve got an empty string. So, the next time you’re API unexpectedly starts returning a null instead of a string, don’t worry about this macro’s integrity.

#define IS_EMPTY_STRING(inString) (!inString || (NSNull *)inString == [NSNull null] || [[inString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:@""] || [[inString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:@"<null>"])

That is all for now.

Written by Sarah

February 4th, 2012 at 8:51 pm

UIView Additions

leave a comment

The most valuable asset a working developer can have is a solid base of reusable code for common occurrences. This often takes the form of well-abstracted classes, but by using categories in Objective-C, you can make the standard Cocoa classes more powerful by adding your own methods. Every so often we’ll put up some of our favorite class additions, which when used together can form the basis of a personalized development framework to make your life easier.

Since the front-end staple of almost any iOS app is the view hierarchy, let’s start with UIView additions that can make your code cleaner and your app look sharper.

I tend to use a few staple methods that I cribbed from the inimitable Joe Hewitt’s original three20 library, particularly:

- (void)setOrigin:(CGPoint)inOrigin {
	[self setFrame:CGRectMake(inOrigin.x, inOrigin.y, self.frame.size.width, self.frame.size.height)];
}

- (void)setSize:(CGSize)inSize {
	[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, inSize.width, inSize.height)];
}

- (void)removeAllSubviews {
	for (UIView *tmpView in [self subviews]) {
		[tmpView removeFromSuperview];
	}
}

Though in general you want your view controllers smart and your views dumb, it’s occasionally useful to know what view controller’s hierarchy a given view is attached to. Your code should never rely on this sort of thing, but during debugging, I find this bad boy really helpful:

- (UIViewController *)viewController {
	for (UIView *next = [self superview]; next; next = next.superview) {
		UIResponder *nextResponder = [next nextResponder];
		if ([nextResponder isKindOfClass:[UIViewController class]]) {
			return (UIViewController *)nextResponder;
		}
	}
	return nil;
}

If you’re dynamically sizing your views or setting the center property, you have to be careful not to set your frames using half-pixels, or they’ll end up blurry. In general it’s better to calculate your frames cleanly, but I’m also a fan of this quick-and-dirty way of making sure you’re not straddling any lines:

- (void)align {
	[self setFrame:CGRectMake((int)self.frame.origin.x, 
                              (int)self.frame.origin.y, 
                              (int)self.frame.size.width, 
                              (int)self.frame.size.height)];
}

Quartz ninja that she is, Sarah has come up with some tasty methods to jazz up your views. Be sure and link to QuartzCore and import <QuartzCore/QuartzCore.h> in the file where you define these methods.

- (void)addShadow {
    if (self.layer.shadowOpacity == 0 && self.width > 0) {
        self.layer.shadowColor = [[UIColor blackColor] CGColor];
        self.layer.shadowRadius = 10.0f;
        CGRect path = CGRectMake(10, self.height - 15, self.width -20, 25);
        self.layer.shadowPath = [[UIBezierPath bezierPathWithRect:path] CGPath];
        
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
        anim.fromValue = [NSNumber numberWithFloat:0.0];
        anim.toValue = [NSNumber numberWithFloat:1.0];
        anim.duration = .2;
        [self.layer addAnimation:anim forKey:@"shadowOpacity"];
        self.layer.shadowOpacity = 1.0;
    }
}

- (void)removeShadow {
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
    anim.fromValue = [NSNumber numberWithFloat:1.0];
    anim.toValue = [NSNumber numberWithFloat:0.0];
    anim.duration = .2;
    [self.layer addAnimation:anim forKey:@"shadowOpacity"];
    self.layer.shadowOpacity = 0.0;
}

- (void)showBounce {
    self.alpha = 0;
    self.hidden = NO;
    [UIView animateWithDuration:0.1 animations:^{self.alpha = 1.0;}];
    self.layer.transform = CATransform3DMakeScale(0.5, 0.5, 1.0);
    
    CAKeyframeAnimation *bounceAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
    bounceAnimation.values = [NSArray arrayWithObjects:
                              [NSNumber numberWithFloat:0.5],
                              [NSNumber numberWithFloat:1.1],
                              [NSNumber numberWithFloat:0.8],
                              [NSNumber numberWithFloat:1.0], nil];
    bounceAnimation.duration = 0.3;
    bounceAnimation.removedOnCompletion = NO;
    [self.layer addAnimation:bounceAnimation forKey:@"bounce"];
    
    self.layer.transform = CATransform3DIdentity;
}

- (void)drawGradiant:(CGRect)rect colors:(NSArray *)inColors{
    
    CGFloat colors[[inColors count]*4]; 
    int i = 0;
    for (UIColor *item in inColors) {
        CGFloat newComponents[4] = {};
        memcpy(newComponents, CGColorGetComponents([item CGColor]), sizeof(newComponents));
        colors[i++] = newComponents[0];
        colors[i++] = newComponents[1];
        colors[i++] = newComponents[2];
        colors[i++] = newComponents[3];
    }
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));
    CGColorSpaceRelease(rgb);
    CGContextSaveGState(context);
    CGContextClipToRect(context, rect);
    CGPoint start = CGPointMake(rect.origin.x, rect.origin.y);  
    CGPoint end = CGPointMake(rect.origin.x, rect.size.height); 
    CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation);
}

Written by Joe

January 24th, 2012 at 7:00 pm

Posted in Uncategorized

Tagged with , ,