64 votes

UIButton block équivalent à addTarget: action: forControlEvents: method?

J'ai regardé autour de moi, mais je n'ai pas trouvé cela sur Internet, ni nulle part dans la documentation Apple. Je suppose donc que cela n'existe pas.

Mais existe-t-il une API iOS4 équivalente à:

 [button addTarget:self action:@selector(tappy:) forControlEvents:UIControlEventTouchUpInside];
 

Je suppose que cela pourrait être implémenté en utilisant une catégorie, mais je préférerais ne pas l'écrire moi-même en raison d'une extrême paresse :)

Quelque chose comme ça serait génial:

 [button handleControlEvent:UIControlEventTouchUpInside withBlock:^ { NSLog(@"I was tapped!"); }];
 

55voto

Martin Reichl Points 578

Je viens de mettre en œuvre cela. Il fonctionne comme un charme!

Et ce n'était même pas difficile.

 typedef void (^ActionBlock)();

@interface UIBlockButton : UIButton {
  ActionBlock _actionBlock;
}

-(void) handleControlEvent:(UIControlEvents)event 
                 withBlock:(ActionBlock) action;

@implementation UIBlockButton

-(void) handleControlEvent:(UIControlEvents)event 
                 withBlock:(ActionBlock) action
{
  _actionBlock = Block_copy(action);
  [self addTarget:self action:@selector(callActionBlock:) forControlEvents:event];
}

-(void) callActionBlock:(id)sender{
  _actionBlock();
}

-(void) dealloc{
  Block_release(_actionBlock);
  [super dealloc];
}
@end
 

24voto

Cfr Points 2525

Il y a une bibliothèque de blocs ajouts à la base commune/classes de l'INTERFACE utilisateur: BlocksKit. Voici la documentation.

Il n'a pas de sous-classe UIButton, mais ajoute UIControl catégorie:

[button addEventHandler:^(id sender) {
    //do something
} forControlEvents:UIControlEventTouchUpInside];

Il y a aussi des blocs/fonctions additionnelles pour les collections (carte, filtre, etc), les vues liées à des trucs et plus.

22voto

Shayne Sweeney Points 458

Voici une implémentation de la catégorie de travail. Dans sa forme actuelle, cela ne devrait être utilisé que dans DEBUG . J'utilise cette catégorie conjointement avec une fonction (incluse ci-dessous) pour tester divers bits de code lorsque l'interaction et le minutage de l'utilisateur sont importants. Encore une fois, ceci est uniquement à des fins de développement / débogage et ne devrait pas être pris en compte pour la production, d'où le #ifdef DEBUG ;)

 #ifdef DEBUG

#import <objc/runtime.h>

static char UIButtonBlockKey;

@interface UIButton (UIBlockButton)

- (void)handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock)block;
- (void)callActionBlock:(id)sender;

@end


@implementation UIButton (UIBlockButton)

- (void)handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock)block {
    objc_setAssociatedObject(self, &UIButtonBlockKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
    [self addTarget:self action:@selector(callActionBlock:) forControlEvents:event];
}


- (void)callActionBlock:(id)sender {
    ActionBlock block = (ActionBlock)objc_getAssociatedObject(self, &UIButtonBlockKey);
    if (block) {
        block();
    }
}

@end


void DSAddGlobalButton(NSString *title, ActionBlock block) {
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setTitle:title forState:UIControlStateNormal];
    [button handleControlEvent:UIControlEventTouchUpInside withBlock:block];
    [button sizeToFit];
    [button setFrame:(CGRect){{100.0f, 100.0f}, [button frame].size}];

    UIView *firstView = [[[[UIApplication sharedApplication] keyWindow] subviews] objectAtIndex:0];
    [firstView addSubview:button];
}


#endif
 

7voto

lavoy Points 600

J'ai créé une bibliothèque pour faire juste cela!

Il prend en charge UIControl (UIButton), UIBarButtonItem, et UIGestureRecognizer. Il est également pris en charge à l'aide de CocoaPods.

https://github.com/lavoy/ALActionBlocks

5voto

Gabe Points 1563

J'AI ÉCRIT CELA IL Y A LONGTEMPS ET CE N'EST PAS LA FAÇON DE RÉSOUDRE CE PROBLÈME!!!!!! Sous-classement UIButton crée un champ de mines qui n'est tout simplement pas la peine. Utilisation Shayne Sweeney Catégorie(je viens de mettre à jour sa réponse avec un tas d'astuces pour rendre son exemple de la production de prêt... j'espère qu'ils faire approuver rapidement).

-----ORIG POST-----

Le code posté par Martin devrait fonctionner si vous êtes seulement de l'affectation de la UIControlEventTouchUpInside... mais il y a quelques problèmes:

  • Vous aurez la fuite des blocs avec le code affiché si vous appelez handleControlEvent: plus d'une fois.
  • Si vous utilisez plus d'un type d'événement, il incendie le dernier bloc pour tous les événements

Dans mon code je suis en s'appuyant sur les Blocs traités comme objet-c objets, qui fonctionne uniquement sous iOS4+(pas 3.2). Il fonctionne bien pour moi quand je veux faire quelque chose de spécial pour les états du bouton(c'est à dire des animations). Vous pouvez simplement utiliser le clickedButton bloc pour la manipulation normale clics.

#import <UIKit/UIKit.h>

@interface ButtWithBlockActions : UIButton {
  void (^downBlock_)(void);
  void (^upBlock_)(void);
  void (^clickedBlock_)(void);
}

@property(nonatomic,retain) void (^downBlock)(void);
@property(nonatomic,retain) void (^upBlock)(void);
@property(nonatomic,retain) void (^clickedBlock)(void);

@end



#import "ButtWithBlockActions.h"

@implementation ButtWithBlockActions

- (void)dealloc {
  [downBlock_ release];
  [upBlock_ release];
  [clickedBlock_ release];
  [super dealloc];
}


- (void (^)(void))downBlock { return downBlock_; }
- (void) fireDownBlock { downBlock_(); }
- (void) setDownBlock:(void (^)(void))block {
  if(downBlock_) {
    [self removeTarget:self action:@selector(fireDownBlock) forControlEvents:UIControlEventTouchDown];
    [self removeTarget:self action:@selector(fireDownBlock) forControlEvents:UIControlEventTouchDragEnter];
    [downBlock_ release];
  }
  downBlock_ = [block copy];
  if(downBlock_) {
    [self addTarget:self action:@selector(fireDownBlock) forControlEvents:UIControlEventTouchDown];
    [self addTarget:self action:@selector(fireDownBlock) forControlEvents:UIControlEventTouchDragEnter];
  }
}


- (void (^)(void))upBlock { return upBlock_; }
- (void) fireUpBlock { upBlock_(); }
- (void) setUpBlock:(void (^)(void))block {
  if(upBlock_) {
    [self removeTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchUpInside];
    [self removeTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchUpOutside];
    [self removeTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchDragOutside];
    [self removeTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchCancel];
    [upBlock_ release];
  }
  upBlock_ = [block copy];
  if(upBlock_) {
    [self addTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchUpInside];
    [self addTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchUpOutside];
    [self addTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchDragOutside];
    [self addTarget:self action:@selector(fireUpBlock) forControlEvents:UIControlEventTouchCancel];
  }
}


- (void (^)(void))clickedBlock { return clickedBlock_; }
- (void) fireClickedBlock { clickedBlock_(); }
- (void) setClickedBlock:(void (^)(void))block {
  if(clickedBlock_) {
    [self removeTarget:self action:@selector(fireClickedBlock) forControlEvents:UIControlEventTouchUpInside];
    [clickedBlock_ release];
  }
  clickedBlock_ = [block copy];
  if(clickedBlock_) {
    [self addTarget:self action:@selector(fireClickedBlock) forControlEvents:UIControlEventTouchUpInside];
  }
}

@end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X