0

I have a custom button class:

CustomButton.h file:

@interface CustomButton : UIButton
@property (nonatomic, retain) NSString* info;
@end

CustomButton.m file:

#import "CustomButton.h"

@implementation CustomButton

@synthesize info;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

@end

In my main view controller:

CustomButton* btn = [CustomButton buttonWithType:UIButtonTypeDetailDisclosure];

[btn setInfo:@"foobar"];
NSLog(@"%@", [btn info]);

[self.view addSubview:btn];

If it's just a simple button ([CustomButton new]) I don't get any error. But if I choose buttonWithType:UIButtonTypeDetailDisclosure I get this error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '-[UIButton setInfo:]: unrecognized selector sent to instance 0x753c8c0'

Why is this happening?

Ilea Cristian
  • 5,741
  • 1
  • 23
  • 37

2 Answers2

1

The buttonWithType: method you are calling is from UIButton, not your CustomButton class. The return value of buttonWithType: is UIButton. Even though you assign this to a variable of type CustomButton it is still a UIButton object. Since UIButton doesn't have an info property or a setInfo: method, you get the error you are seeing.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • So how do I create a buttonWithType AND add other proprieties/functionalities ? – Ilea Cristian Feb 11 '13 at 23:25
  • That's a good question. I just answered why you were having the problem. I don't know a good solution. Overriding `UIButton` is tricky unless you can use the standard `initWithFrame:` method to create it. – rmaddy Feb 11 '13 at 23:30
1

As soon as UIButton does not provide initWithType: method - you can't subclass a "typed" button. You can't create an extension for a library class either. The only way to "attach" something to a predefined object is to use associated objects:

#import <objc/runtime.h>
    
UIButton* btn = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
NSString* info = @"foobar";
objc_setAssociatedObject(btn, "info", info, OBJC_ASSOCIATION_RETAIN);
//Later somewhere
NSString* btnInfo = (NSString*)objc_getAssociatedObject(btn, "info");

The "info" can be any string you like, it's just a key to retrieve that object later. OBJC_ASSOCIATION_RETAIN means that the object would be retained and automatically released after the btn object's dealloc: would be called. You can find more details about that here.

The other way to solve your issue is to subclass the UIButton, add your info property and make it look like disclosure button by setting custom image with setImage:forState: method.

Generally, coupling some data with standard UI controls is a sign of a bad architecture. Maybe you'll make a little step backwards and try to find some other way to pass that string wherether you need to use it?

Community
  • 1
  • 1
Anton Chikin
  • 1,746
  • 2
  • 17
  • 22