Call super in overridden class method [duplicate]
I want to add a new custom UIButtonType
to the UIButton
class via a category like so:
enum {
UIButtonTypeMatteWhiteBordered = 0x100
};
@interface UIButton (Custom)
+ (id)buttonWithType:(UIButtonType)buttonType;
@end
Is it possible to get the super
impleme开发者_开发问答ntation of that overridden method somehow?
+ (id)buttonWithType:(UIButtonType)buttonType {
return [super buttonWithType:buttonType];
}
The code above is not valid since super
refers to UIControl
in this context.
You can replace the method at runtime with your own custom method like so:
#import <objc/runtime.h>
@implementation UIButton(Custom)
// At runtime this method will be called as buttonWithType:
+ (id)customButtonWithType:(UIButtonType)buttonType
{
// ---Add in custom code here---
// This line at runtime does not go into an infinite loop
// because it will call the real method instead of ours.
return [self customButtonWithType:buttonType];
}
// Swaps our custom implementation with the default one
// +load is called when a class is loaded into the system
+ (void) load
{
SEL origSel = @selector(buttonWithType:);
SEL newSel = @selector(customButtonWithType:);
Class buttonClass = [UIButton class];
Method origMethod = class_getInstanceMethod(buttonClass, origSel);
Method newMethod = class_getInstanceMethod(buttonClass, newSel);
method_exchangeImplementations(origMethod, newMethod);
}
Be careful how you use this, remember that it replaces the default implementation for every single UIButton your app uses. Also, it does override +load, so it may not work for classes that already have a +load method and rely on it.
In your case, you may well be better off just subclassing UIButton.
Edit: As Tyler notes below, because you have to use a class level method to make a button this may be the only way to override creation.
Jacob has a good point that category methods act differently than subclass methods. Apple strongly suggests that you only provide category methods that are entirely new, because there are multiple things that can go wrong otherwise - one of those being that defining a category method basically erases all other existing implementations of the same-named method.
Unfortunately for what you're trying to do, UIButton
seems to be specifically designed to avoid subclassing. The only sanctioned way to get an instance of a UIButton
is through the constructor [UIButton buttonWithType:]
. The problem with a subclass like Jacob suggests (like this):
@implementation MyCustomButton
+ (id)buttonWithType:(UIButtonType)buttonType {
return [super buttonWithType:buttonType]; //super here refers to UIButton
}
@end
is that the type returned by [MyCustomButton buttonWithType:]
will still be a UIButton
, not MyCustomButton
. Because Apple hasn't provided any UIButton
init methods, there's not really a way for a subclass to instantiate itself and be properly initialized as a UIButton
.
If you want some customized behavior, you can create a custom UIView
subclass that always contains a button as a subview, so that you can take advantage of some of UIButton
's functionality.
Something like this:
@interface MyButton : UIView {}
- (void)buttonTapped;
@end
@implementation MyButton
-(id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = self.bounds;
[button addTarget:self action:@selector(buttonTapped)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
return self;
}
- (void)buttonTapped {
// Respond to a button tap.
}
@end
If you want the button to do different things depending on more complex user interactions, you can make more calls to [UIButton addTarget:action:forControlEvents:]
for different control events.
Reference: Apple's UIButton class reference
No, this is not possible when you use a category to augment a class' functionality, you are not extending the class, you are actually wholly overriding the existing method, you lose the original method completely. Gone like the wind.
If you create a subclass of UIButton
, then this is totally possible:
enum {
UIButtonTypeMatteWhiteBordered = 0x100
};
@interface MyCustomButton : UIButton {}
@end
@implementation MyCustomButton
+ (id)buttonWithType:(UIButtonType)buttonType {
return [super buttonWithType:buttonType]; //super here refers to UIButton
}
@end
精彩评论