Blog Archives

UIAppearance Is Better Than You Think

You already know about the UIAppearance protocols added to iOS. You know that UIAppearance makes it WAY simpler to customize the appearance of the common UI elements is iOS. You probably know that you can customize the appearance of the duly marked UI_APPEARANCE_SELECTOR methods on your own subclasses as a means of differentiating certain elements from others of the same type (such as specifying a class of UIBarButtonItem as a cancel button and therefore always red).

What you might not know is that it’s trivial to use UI_APPEARANCE_SELECTOR on your own custom subclasses for visual elements that are not in the standard set of appearance selectors.  Here’s a quick example of how you might use this:

Say you have a particular class of view you have all over your application that has border around it (using the CALayer backing the view).  You could hard code this look into a view subclass, or you could do it the hard way by setting this on every individual view.  You can also use a UI_APPEARANCE_SELECTOR on your custom class and set it with an appearance proxy.  This functionality and behavior falls squarely under “I can’t believe it’s this easy”.

Here’s the interface:

#import <UIKit/UIKit.h>

@interface TestView : UIView

@property (nonatomic, retain) UIColor *backgroundColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, retain) UIColor *borderColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, retain) UIFont *font UI_APPEARANCE_SELECTOR;
@end

Here’s the implementation:

#import "TestView.h"
#import <QuartzCore/QuartzCore.h>

@implementation TestView
@dynamic backgroundColor;
@dynamic borderColor;
@synthesize font = _font;

- (void)drawRect:(CGRect)rect
{
    NSLog(@"%@", self.font);
}

- (void)setBackgroundColor:(UIColor *)backgroundColor
{
    [super setBackgroundColor:backgroundColor];
}

- (UIColor *)backgroundColor
{
    return [super backgroundColor];
}

- (void)setBorderColor:(UIColor *)borderColor
{
    self.layer.borderWidth = 4.0;
    self.layer.borderColor = [borderColor CGColor];
}

- (UIColor *)borderColor
{
    return [UIColor colorWithCGColor:self.layer.borderColor];
}

@end

And in the App Delegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    self.window.backgroundColor = [UIColor whiteColor];

    MainViewController *mainView = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];

    self.window.rootViewController = mainView;
    [self.window makeKeyAndVisible];

    [[TestView appearance] setBackgroundColor:[UIColor blueColor]];
    [[TestView appearance] setBorderColor:[UIColor redColor]];
    [[TestView appearance] setFont:[UIFont systemFontOfSize:12]];
    return YES;
}

And here’s the result:

And the NSLog prints the font that was set:

2012-05-16 17:03:43.517 test[26634:f803] font-family: “Helvetica”; font-weight: normal; font-style: normal; font-size: 12px

What’s the advantage?

Well it depends on your development strategy and the scope of the project.  For large, running projects it’s helpful to separate as much of the presentation code (fonts and colors) from the logic of the application.  This allows you to keep all of your styles centralized in one place as opposed to littered throughout a bunch disparate classes in much the same way that CSS can separate presentation logic from the HTML structure of a website.  Additionally, if you’re writing framework level code for a static library it offers greater flexibility and code reuse where you’re not as able to edit the implementation of a class.  This of course all depends on the purpose of your app.  For once-and-done style apps it may not make quite as much sense.

Technical notes:

You will find that if you tinker around that the UI_APPEARANCE_SELECTOR macro isn’t really necessary.  In fact, the code above will work just fine without it.  The macro #define’s to nothing, so I’m not personally sure if there’s more grandiose future plans for if it’s just a placebo macro to let developers know what will be guaranteed to be supported.  In truth, virtually any display related property on a UIView can already be set using UIAppearance proxies, though like all non-official APIs Apple can change that without notice.  By taking the approach above you’re likely future proofing your code for later.  If I were to take a guess at the internals of UIAppearance it seems like a clever hack on KVC more than a real, run-time enforced protocol.

Regardless, there’s a lot of power to be exploited using this protocol and it should change the way you think about creating a great visual style for your apps.

Advertisements

6 Things in iOS 5 That Will Make Your Next Project Easier

There’s no shortage of hype around the arrival of iOS5 this week.  Much of it has been about iCloud, ARC, notifications, and Siri of course.  These are big deals to be sure, but for a coder there’s some great improvements that simply make your development process easier.  Here’s my list of favorites, some of which I’ve already started using:

CoreImage and CIFilters – The addition of the CoreImage framework will give you more ways to create compelling UIs.  CIFiltlers aren’t limited just to images.  You can apply them to any CALayer.

UIAppearance – It used to be that if you wanted a custom looking UISwitch or or UIProgressView you’d have to implement it from scratch or try and rip the control apart view by view and modify it yourself.  Writing a custom control is of course time consuming and modifying the view hierarchy leaves your UI at the mercy of the next iOS update.  UIAppearance and the appearance proxies let you configure the look of the default controls in ways you couldn’t before either on a per instance or hierarchy level, or application wide.

Container View Controllers – Absolutely huge, probably my the most useful improvement for me.  It used to be that if you wanted a custom container, like a tab bar or split view, you had to implement it yourself from scratch.  It was always problematic and you often had to hack properties like myParentViewController because the property was locked down.  Now you can make container view controllers and the existing controllers (and containers like UINavigationController) will play nice with them.

Real locations in the simulator – You’re probably not really at Apple headquarters anyway.  Glad the simulator has finally caught on 😉

Ordered relationships in CoreData – If you’ve ever tried to do this on your own, you know how valuable this can be.  Not only does your “group” have “users”, they now have an ordered collection of “users”.

EventKit upgrades – What strikes me a severe limitation of EventKit is that up until now you can’t create calendars in your iOS app.  EventKit upgrades now allow it.