2010-12-03 5 views
3

Je veux ajouter une nouvelle coutume UIButtonType à la classe UIButton par une catégorie comme ceci:super appel en méthode de classe surchargée

enum { 
    UIButtonTypeMatteWhiteBordered = 0x100 
}; 

@interface UIButton (Custom) 

+ (id)buttonWithType:(UIButtonType)buttonType; 

@end 

Est-il possible d'obtenir la mise en œuvre super de cette méthode surchargée en quelque sorte?

+ (id)buttonWithType:(UIButtonType)buttonType { 
    return [super buttonWithType:buttonType]; 
} 

Le code ci-dessus n'est pas valide car super fait référence à UIControl dans ce contexte.

Répondre

3

Non, cela est impossible lorsque vous utilisez une catégorie pour augmenter une fonctionnalité de classe, vous n'êtes pas extension la classe, vous êtes réellement surchargez totalement la méthode existante, vous perdez la méthode originale complètement. Parti comme le vent.

Si vous créez une sous-classe de UIButton, cela est tout à fait possible:

enum { 
    UIButtonTypeMatteWhiteBordered = 0x100 
}; 

@interface MyCustomButton : UIButton {} 

@end 

@implementation MyCustomButton 

+ (id)buttonWithType:(UIButtonType)buttonType { 
    return [super buttonWithType:buttonType]; //super here refers to UIButton 
} 

@end 
+0

Subclassing ne fonctionne pas, parce que 'UIButton' est un cluster de classe. Vous pouvez surcharger cette méthode sur une sous-classe, mais vous devez alors explicitement appeler '[MyCustomButton buttonWithType: ...]', et si vous le faites, vous pouvez aussi simplement 'all/init' toi même. La meilleure réponse est de faire pivoter l'IMP –

+1

Ahh Objective C, le seul langage où l'on peut satisfaire un penchant pour imp swizzling. –

5

Vous pouvez remplacer la méthode à l'exécution avec votre propre méthode personnalisée comme ceci:

#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); 
} 

Soyez prudent vous l'utilisez, n'oubliez pas qu'il remplace l'implémentation par défaut pour chaque UIButton que votre application utilise. En outre, il surcharge + load, donc il peut ne pas fonctionner pour les classes qui ont déjà une méthode + load et s'en remettre.

Dans votre cas, il vaudrait peut-être mieux vous contenter de sous-classer UIButton.

Modifier: Comme le note Tyler ci-dessous, parce que vous devez utiliser une méthode de niveau classe pour créer un bouton, cela peut être le seul moyen de surcharger la création.

+0

@Kendall Ne pensez-vous pas que c'est un peu trop un hack? Sous-classement IMHO est * tellement * beaucoup plus simple et plus facile à déboguer. –

+0

Habituellement, oui. Mais il y a certaines choses où il est bon de changer le système de comportement large ... notez que j'ai recommandé de sous-classer dans ce cas. Je pensais juste que les gens devraient savoir que c'était réellement possible en cas de besoin, puisque le sujet de la question était assez général. –

+0

C'est une technique géniale. Comment le runtime sait-il appeler la méthode originale depuis la nouvelle méthode? Pouvez-vous empiler arbitrairement-de nombreuses implémentations de cette façon? – Tyler

3

Jacob a un bon point que les méthodes de catégorie agissent différemment des méthodes de sous-classe. Apple suggère fortement que vous ne fournissiez que des méthodes de catégorie entièrement nouvelles, car il y a plusieurs choses qui peuvent mal se passer autrement - l'une d'elles étant que la définition d'une méthode de catégorie efface toutes les autres implémentations existantes de la même méthode.

Malheureusement pour ce que vous essayez de faire, UIButton semble avoir été spécialement conçu pour éviter les sous-classes. La seule manière sanctionnée pour obtenir une instance d'un UIButton est à travers le constructeur [UIButton buttonWithType:]. Le problème avec une sous-classe comme Jacob suggère (comme ça):

@implementation MyCustomButton 

+ (id)buttonWithType:(UIButtonType)buttonType { 
    return [super buttonWithType:buttonType]; //super here refers to UIButton 
} 

@end 

est que le type retourné par [MyCustomButton buttonWithType:] sera toujours un UIButton, pasMyCustomButton. Comme Apple n'a fourni aucune méthode d'initialisation UIButton, il n'existe pas vraiment de moyen pour une sous-classe de s'instancier elle-même et d'être correctement initialisée en tant que UIButton. Si vous voulez un comportement personnalisé, vous pouvez créer une sous-classe personnalisée UIView qui contient toujours un bouton en tant que sous-vue, afin que vous puissiez profiter de certaines fonctionnalités de UIButton.

Quelque chose comme ceci:

@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 

Si vous voulez que le bouton de faire des choses différentes en fonction des interactions utilisateur plus complexes, vous pouvez faire plus d'appels à [UIButton addTarget:action:forControlEvents:] pour les événements de contrôle différents.

Référence: Apple's UIButton class reference

+0

UIButton peut être instancié à l'aide de init. http://stackoverflow.com/q/6245882/772481 – angelokh

+0

Bien sûr, mais comment définir buttonType? – Olie

Questions connexes