2

Is it possible to use iOS 5 appearance proxies to refactor code that sets properties on layers?

_button.layer.cornerRadius = 5.0f;
_button.layer.borderWidth = 1.0f;
_button.layer.borderColor = [[UIColor blackColor] CGColor];
_button.layer.masksToBounds = YES;
ktingle
  • 374
  • 2
  • 9
  • 1
    I feel that at the moment, the wrong answer is marked as correct :-) [Tieme's answer](http://stackoverflow.com/a/8955194/2547229) is correct specifically for a vanilla UIButton. However, if you're happy to extend UIButton in the trivial way shown in [Sandy's answer](http://stackoverflow.com/a/23891904/2547229), you can achieve the effects you ask for in your question. – Benjohn Dec 12 '14 at 21:47

2 Answers2

7

The accepted answer is not correct. You can set properties on layers, however, you need to subclass the view and expose the layer properties through accessors.

To illustrate, I'll use just one property from the question, cornerRadius:

Step 1: Implement a UIButton subclass.

#import <UIKit/UIKit.h>

@interface MyRoundedCornerButton : UIButton

@end

Step 2: Add a property tagged with UI_APPEARANCE_SELECTOR.

#import <UIKit/UIKit.h>

@interface MyRoundedCornerButton : UIButton

@property (readwrite, nonatomic) CGFloat cornerRadius UI_APPEARANCE_SELECTOR;

@end

Step 3: Implement the new class.

@implementation MyRoundedCornerButton

- (void)setCornerRadius:(CGFloat)cornerRadius
{
    self.layer.cornerRadius = cornerRadius;
}

@end

Step 4: Set the corner radius in the appearance proxy.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    [MyRoundedCornerButton appearance].cornerRadius = 10.0;
    ...
}

Step 5: Then in IB, (or wherever you define the creation of the View), set the custom view class to (or instantiate an instance of) MyRoundedCornerButton instead of UIButton.

Notes: I've done this to apply an easily changeable gradient background throughout my app. In my case, all view controllers' root view use a custom class. This custom class provides a CAGradientLayer through the +(Class)layerClass method. Then, I expose the colors and locations properties of the underlying gradient layer using the UI_APPEARANCE_SELECTOR tag. Setting it once on app initialization customizes the entire app. You could even expose the colors to the user to allow them to fully customize colors of various controls.

Sandy Chapman
  • 11,133
  • 3
  • 58
  • 67
  • Thanks! Should it not be `MyRoundedCornerButton : UIButton`, though? – Benjohn Dec 12 '14 at 16:44
  • Another unrelated suggestion – if you use an `NSNumber` for the corner radius's type, you will also be able to override the appearance default from IB using `User Defined Runtime Attributes`. – Benjohn Dec 12 '14 at 17:05
  • 1
    @Benjohn, you're correct, it should be UIButton (I'll update my answer). There are some caveats around using the `User Defined Runtime Attributes` for setting these values (for instance when you have multiple UI_APPEARANCE_SELECTOR members which influence each other), but for the most part, you won't hit those problems. Also, something new that applies to this is that if you use Swift, you no longer need the `UI_APPEARANCE_SELECTOR` annotation and you can expose values using the `@IBInspectable` to your members to have them more easily editable in IB. – Sandy Chapman Dec 12 '14 at 17:29
  • I was wondering if it's possible to add these UI_APPEARANCE_SELECTOR methods in a category so they could be provided for all UIView instances. I'm guessing **"No!"**, but hey, it'd be net if you could, right?! – Benjohn Dec 12 '14 at 21:48
  • 2
    @Benjohn, In fact you CAN add them to categories. I've done just that for UIViews to add a borderWidth and borderColor property that can be set on any UIView in my application. – Sandy Chapman Dec 15 '14 at 11:36
2

Nope.. Apple's says:

To support appearance customization, a class must conform to the UIAppearanceContainer protocol and relevant accessor methods must be marked with UI_APPEARANCE_SELECTOR.

http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAppearance_Protocol/Reference/Reference.html

And UIButton does not.

EDIT: And UIButton does not have any methods marked with UI_APPEARANCE_SELECTOR.

Tieme
  • 62,602
  • 20
  • 102
  • 156
  • 3
    UIButton does in fact support Appearance Proxies, in fact you can see UIButton being used in the demo Apple gave in the session "Customizing the Appearance of UIKit Controls" delivered at WWDC in 2011. Link https://developer.apple.com/videos/wwdc/2011/ (look about 28 min in) – Jarson Jan 24 '12 at 00:31
  • Is the OP not asking about a control's view's CALayer, not specifically about UIButton? –  Jun 22 '12 at 09:03