By MatterGoal


2011-03-31 13:07:19 8 Comments

I'd like to subclass UIButton to add some properties that i need (not methods... only properties).

Here the code of my subclass:

//.h-----------------------
@interface MyButton : UIButton{
    MyPropertyType *property;
}

@property (nonatomic,retain) MyPropertyType *property;
@end

//.m--------------------------
@implementation MyButton
@synthesize property;

@end

And here how I use the class:

MyButton *btn = ((MytButton *)[MyButton buttonWithType:UIButtonTypeRoundedRect]);
btn.property = SomeDataForTheProperty;

From where i obtain this error :

 -[UIRoundedRectButton setProperty:]: unrecognized selector sent to instance 0x593e920

Thus, from ButtonWithType i obtain a UIRoundedRectButton and (Mybutton *) can't cast it... What i have to do to obtain a MyButton object ? is -init the unique solution ?

Thank you!

3 comments

@Joe 2011-03-31 13:21:22

Try using a category with Associative References instead. It is much cleaner and will work on all instances of UIButton.

UIButton+Property.h

#import <Foundation/Foundation.h>

@interface UIButton(Property)

@property (nonatomic, retain) NSObject *property;

@end

UIButton+Property.m

#import "UIButton+Property.h"
#import <objc/runtime.h>

@implementation UIButton(Property)

static char UIB_PROPERTY_KEY;

@dynamic property;

-(void)setProperty:(NSObject *)property
{
    objc_setAssociatedObject(self, &UIB_PROPERTY_KEY, property, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(NSObject*)property
{
    return (NSObject*)objc_getAssociatedObject(self, &UIB_PROPERTY_KEY);
}

@end

//Example usage

#import "UIButton+Property.h"

...

UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button1.property = @"HELLO";
NSLog(@"Property %@", button1.property);
button1.property = nil;
NSLog(@"Property %@", button1.property);

@Joe 2011-03-31 13:27:55

I used NSObject but the type can be any type you define including MyPropertyType also UIButton(Property) is really generic consider renaming property to something more useful.

@MatterGoal 2011-04-04 09:04:45

This's a clean way to do exactly what i'm asking for :) thank you

@Chris Brandsma 2011-06-21 18:43:19

I didn't use your answer, but I learned something new (objc_setAssociatedObject). So thank you.

@roocell 2011-12-25 14:43:43

excellent - this works great for using UIButtonTypeContactAdd at the end of a row and passing objects. (when .tag isn't sufficient).

@Anonymous White 2012-10-28 12:00:40

Does this work for iOS5?

@Anonymous White 2012-10-28 12:01:10

where did you initialize static char UIB_PROPERTY_KEY;?

@Joe 2012-10-28 12:31:31

You do not need to initialize it, the address is the key.

@Anonymous White 2012-10-28 12:54:03

all we care is that it's different. Also can we combine this with sub classing for truly elegant solution.

@Anonymous White 2012-10-28 13:11:58

Looks like this thing won't work with subclassing isn't it. For all IOS know the object is of type UIButton and unless you add the category to the class, which would defeat the purpose of encapsulating, it'll still complain.

@AdamM 2012-11-13 16:37:07

This solution worked great for me, originally I was going to subclass, but this method is soo much cleaner. Cheers!!!

@João Nunes 2013-02-11 13:30:07

I dont really think this is a clean solution... This is a hacky solution instead.

@Joe 2013-02-11 14:25:11

@JoãoNunes Care to explain what is "hacky" about a well documented solution?

@João Nunes 2013-02-13 08:07:58

using objc low-level methods is not pretty at all and looks hacky, subclassing is much more objective programming and less confusing.

@David John Smith 2013-07-01 00:49:11

In the case of UIButton I think this is a much better solution than subclassing, as calling buttonWithType: on a subclass will not return an object with the inherited methods.

@João Nunes 2013-12-19 09:47:23

ofc... you have to subclass the initialiser too! To create a different object otherwise you are just creating a UIButton.

@Luke 2014-01-21 04:22:15

Is it allowed - Would an App get approved using this?

@jeet.chanchawat 2015-12-23 12:54:55

For future users: dont miss #import <objc/runtime.h> to avoid getting into this trouble stackoverflow.com/questions/30025593/…

@StCredZero 2013-01-24 05:19:43

I have a simple scheme that only involves a few library methods, no boilerplate, and just 3 lines of code for each property you want to add. There are two example properties added below: startPoint and tileState. For illustrative purposes here are the lines you'd need to add for a property like tileState:

//@property (assign, nonatomic) SCZTileState tileState; // tileState line 1 
//@property (assign, nonatomic) SCZTileState tileState; // tileState line 2 
//@dynamic tileState;                                   // tileState line 3

There's more details in my blog post describing how this works

UIButton+SCZButton.h

#import <UIKit/UIKit.h>

@interface UIButton (SCZButton)
@property (readwrite, nonatomic) id assocData;
@end

UIButton+SCZButton.m

//  UIButton+SCZButton.m
//  Copyright (c) 2013 Ooghamist LLC. All rights reserved.

#import "UIButton+SCZButton.h"
#import <objc/runtime.h>

@implementation UIButton (SCZButton)
- (id)assocData {
    id data = objc_getAssociatedObject(self, "SCZButtonData");
    return data;
}
- (void)setAssocData:(id)data {
    objc_setAssociatedObject(self, "SCZButtonData", data,  
                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

OOGTotallyTile.h

//  UIButton+OOGTotallyTile.m
//  Copyright (c) 2013 Ooghamist LLC. All rights reserved.
#import <UIKit/UIKit.h>
#import "UIButton+SCZButton.h"
#define kPointLabelTag 837459

typedef enum {
    SCZTileStatePlaced,
    SCZTileStateDropping,
    SCZTileStateDropped
} SCZTileState;

@interface SCZButtonData : NSObject
@property (assign, nonatomic) CGPoint startPoint;
@property (assign, nonatomic) SCZTileState tileState;   // tileState line 1
@end

@interface UIButton (OOGTotallyTile)
@property (readonly, nonatomic) SCZButtonData *buttonData;
@property (assign, nonatomic) CGPoint startPoint;
@property (assign, nonatomic) SCZTileState tileState;  // tileState line 2
@end

OOGTotallyTile.m

//  UIButton+OOGTotallyTile.m
//  Copyright (c) 2013 Ooghamist LLC. All rights reserved.

#import "OOGTotallyTile.h"

@implementation SCZButtonData
@end

@implementation UIButton (OOGTotallyTile)
@dynamic startPoint;
@dynamic tileState; // tileState line 3

- (SCZButtonData*)buttonData {
    if ( ! self.assocData) {
        self.assocData = [[SCZButtonData alloc] init];
    }
    return self.assocData;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
    id forwardingTarget = [super forwardingTargetForSelector:aSelector];
    if ( ! forwardingTarget) {
        return [self buttonData];
    }
    return forwardingTarget;
}
@end

@Wex 2011-03-31 13:12:10

You need to do:

MyButton *btn = [[MyButton alloc] init];

To create your button. The buttonWithType:UIButtonTypeRoundedRect only creates UIButton objects.

=== edit ===

If you wish to use a RoundedRect button; then I would suggest the following. Basically, we just create a UIView with whatever properties we want and add the desired button to that view.


.h

@interface MyButton : UIView
{
    int property;
}

@property int property;
@end

.m

@implementation MyButton
@synthesize property;

- (id)initWithFrame:(CGRect)_frame
{
    self = [super initWithFrame:_frame];
    if (self)
    {
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        btn.frame = self.bounds;
        [self addSubview:btn];
    }
    return self;
}

@end

Usage:

MyButton *btn = [[MyButton alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
btn.property = 42;

[self.view addSubview:btn];

@Joe 2011-03-31 15:25:42

Doesn't this method limit you to using buttons of type UIButtonTypeCustom?

@Wex 2011-03-31 15:32:52

Updated my answer.

@Joe 2011-03-31 16:57:17

Now he has no access to any button methods unless he exposes btn through a property.

@Dan Rosenstark 2012-02-09 20:57:50

@Joe: okay, it could be promoted to a property, too. Or you could hide some of the UIButton functionality. Great Facade, Wex.

Related Questions

Sponsored Content

33 Answered Questions

[SOLVED] How do I create a basic UIButton programmatically?

10 Answered Questions

[SOLVED] How to set the title of UIButton as left alignment?

  • 2010-05-04 11:56:19
  • Madan Mohan
  • 185585 View
  • 426 Score
  • 10 Answer
  • Tags:   objective-c uibutton

3 Answered Questions

[SOLVED] Property not found on object of type

2 Answered Questions

UIButton deallocated issue

2 Answered Questions

[SOLVED] Custom UIButton does not change its image

0 Answered Questions

How to update the selected property of a UIButton using the button's tag

  • 2013-12-11 23:10:43
  • Adam M
  • 208 View
  • 0 Score
  • 0 Answer
  • Tags:   ios uibutton

1 Answered Questions

[SOLVED] Subclassing NSMapTable

1 Answered Questions

[SOLVED] custom UIButton, unrecognized selector sent to instance

1 Answered Questions

Sponsored Content