10

I have some custom appearance properties in my view class (a descendant of UIView). I want to customize the view appearance according to these properties, but I can’t do that inside the initializer, since the values set using [[MyClass appearance] setFoo:…] aren’t in effect at that point:

@interface View : UIView
@property(strong) UIColor *someColor UI_APPEARANCE_SELECTOR;
@end

@implementation View
@synthesize someColor;

// Somewhere in other code before the initializer is called:
// [[View appearance] setSomeColor:[UIColor blackColor]];

- (id) initWithFrame: (CGRect) frame
{
    self = [super initWithFrame:frame];
    NSLog(@"%@", someColor); // nil
    return self;
}

@end

They are already set in layoutSubviews, but that’s not a good point to perform the view customizations, since some customizations may trigger layoutSubviews again, leading to an endless loop.

So, what’s a good point to perform the customizations? Or is there a way to trigger the code that applies the appearance values?

zoul
  • 102,279
  • 44
  • 260
  • 354
  • I think once the custom class has been allocated , the properties of member can always be accessed and changed as per requirement and then the object can be used with the modified information in the code. – Abhishek Singh May 24 '12 at 12:28
  • 1
    Just to make sure, I am talking about the properties set through the appearance proxy (`UIAppearance`). These values are set somewhere later than in the initializer. If I set a breakpoint on the property setter, I can see that the values are applied from `[CALayer layoutSublayers]`. – zoul May 24 '12 at 12:33

5 Answers5

2

One possible workaround is to grab the value directly from the proxy:

- (id) initWithFrame: (CGRect) frame
{
    self = [super initWithFrame:frame];
    NSLog(@"%@", [[View appearance] someColor); // not nil
    return self;
}

Of course this kills the option to vary the appearance according to the view container and is generally ugly. Second option I found is to perform the customizations in the setter:

- (void) setSomeColor: (UIColor*) newColor
{
    someColor = newColor;
    // do whatever is needed
}

Still I’d rather have some hook that gets called after the appearance properties are set.

zoul
  • 102,279
  • 44
  • 260
  • 354
1

Why not wait until

- (void)willMoveToSuperview:(UIView *)newSuperview {
    [super willMoveToSuperview:newSuperview];

    if (newSuperview) {
        ... code here ...
    }
}

if it's giving you trouble?

deanWombourne
  • 38,189
  • 13
  • 98
  • 110
1

I believe UIAppearance properties are applied to a view when it is being added into a view hierarchy. So presumably you could access the set properties in UIView didMoveToSuperview.

TomSwift
  • 39,369
  • 12
  • 121
  • 149
1

Caveat: I am using Swift 2, so not sure about earlier versions of Swift / Objective-C. But I have found that didMoveToSuperview() will not work. The properties are available in layoutSubviews(), but that's not a great place to do anything like this (since it can be called more than once). The best place to access these properties in the lifeCycle of the view I have found is didMoveToWindow().

TMart
  • 39
  • 2
0

I would have thought that viewDidLoad would be best if it's a one-time thing. Otherwise, viewWillAppear.

EDIT:

If you want to do it in the view, and not it's controller then I would create a custom init for the view along the lines of:

-(id) initWithFrame:(CGRect) frame andAppearanceColor:(UIColor)theColor;

thereby passing the colour into the view at creation time.

ader
  • 5,403
  • 1
  • 21
  • 26
  • These are controller methods and I’d much rather handle it in the view. Besides, it’s quite probable that the values are not set yet during these calls. – zoul May 24 '12 at 14:04
  • Passing the color in the initializer is also not an option, since there are more appearance properties to set and the initializer is called in a place out of the controller’s… erm, *control*. One could say that the whole point of the appearance proxy API was to get rid of setting the values by hand all the time. – zoul May 24 '12 at 14:30
  • I see where you're coming from. Maybe you could create a method to set the appearance properties and call it in response to an NSNotification (sent when the appearance properties are specified)? – ader May 24 '12 at 14:40
  • At the time when the appearance properties are set through `[[View appearance] setSomeColor:…]`, the instances of `View` don’t exist yet, so unfortunately that’s not an option either. – zoul May 24 '12 at 14:46