相当于addTarget的UIButton块:action:forControlEvents:method?
我环顾四周,但无法在互联网上find这个,也没有在苹果文档中的任何地方,所以我猜测它不存在。
但有没有一个iOS4块等效的API来:
[button addTarget:self action:@selector(tappy:) forControlEvents:UIControlEventTouchUpInside];
我想这可以使用一个类来实现,但宁愿不要自己写这个,因为极度的懒惰:)
像这样的东西会很棒:
[button handleControlEvent:UIControlEventTouchUpInside withBlock:^ { NSLog(@"I was tapped!"); }];
我刚刚实现了这一点。 它的工作就像一个魅力!
而且这还不是很难。
typedef void (^ActionBlock)(); @interface UIBlockButton : UIButton { ActionBlock _actionBlock; } -(void) handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action; @end @implementation UIBlockButton -(void) handleControlEvent:(UIControlEvents)event withBlock:(ActionBlock) action { _actionBlock = action; [self addTarget:self action:@selector(callActionBlock:) forControlEvents:event]; } -(void) callActionBlock:(id)sender{ _actionBlock(); } @end
有一个块添加到常见的基础/ UI类: BlocksKit 。 这里是文档 。
它不是UIButton的子类,而是添加了UIControl类 :
[button addEventHandler:^(id sender) { //do something } forControlEvents:UIControlEventTouchUpInside];
也有集合(地图,filter等),视图相关的东西和更多的块/function添加。
注意:它不适合与Swift。
这是一个工作类别实现。 在目前的forms中,这只能在DEBUG
。 当用户交互和定时很重要时,我将这个类别与一个函数(下面包含)结合使用来testing各种代码。 再次,这只是为了开发/debugging的目的,不应该考虑生产,因此#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
我创build了一个图书馆来做到这一点!
它支持UIControl
( UIButton
), UIBarButtonItem
和UIGestureRecognizer
。 它也支持使用CocoaPods。
https://github.com/lavoy/ALActionBlocks
// Assuming you have a UIButton named 'button' [button handleControlEvents:UIControlEventTouchUpInside withBlock:^(id weakControl) { NSLog(@"button pressed"); }];
安装
pod 'ALActionBlocks'
我写了这个长期的AGO,这不是解决这个问题的方法! UIButton的子类创build一个不值得的雷区。 使用Shayne Sweeney的分类(我刚刚更新了他的答案,做了一些调整,使他的示例生产准备好了,希望他们能很快得到批准)。
—– ORIG POST —–
马丁公布的代码应该工作,如果你只是分配UIControlEventTouchUpInside …但有几个问题:
- 如果您调用handleControlEvent,您将泄漏代码块:不止一次。
- 如果分配了多个types的事件,它将触发所有事件的最后一个块
在我的代码中,我依靠被视为对象c对象的块,它只能在iOS4 +(不是3.2)上运行。 当我想为button状态(即animation)做一些特殊的事情时,它对我很好。 您可以使用clickedButton块来处理正常的点击。
#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
有REKit带出块潜力。 它使您能够使用Block向实例添加/覆盖方法。
使用REKit,您可以dynamic地制作一个目标 – 响应buttonAction – 如下所示:
id target; target = [[NSObject alloc] init]; [target respondsToSelector:@selector(buttonAction) withKey:nil usingBlock:^(id receiver) { // Do something… }]; [button addTarget:target action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
您不需要创build子类或类别。
除了目标/动作范例,您还可以使用REKit进行委派模式。
我发现使用一个小帮手类很容易和多才多艺:
@interface Handler : NSObject @end @implementation Handler { void (^block)(id); } + (Handler *)create:(void (^)(id))block { Handler *result = [[Handler alloc] init]; result->block = block; return result; } - (void)call:(id)sender { block(sender); } @end
并像这样使用它:
Handler *handler = [Handler create:^(id sender) { // ... handle the event, using local state captured by the block ... }]; // store the handler because the target is not retained in addTarget [handlers addObject:handler]; [button addTarget:handler action:@selector(call:) forControlEvents:UIControlEventTouchUpInside];
在上面的Shayne Sweeney的post的帮助下,我创build了一个答案组合。
我已经把它上传到Github这里: https : //github.com/AvdLee/UIControl-Blocks
Swift扩展/基于类别的实现,我鞭打。 使用OBJC关联的对象不是反模式。 :P
import UIKit // MARK: UIControl Block based actions typealias ActionBlock = (UIControl) -> () class UIButtonActionDelegate : NSObject { let actionBlock : ActionBlock init(actionBlock: ActionBlock) { self.actionBlock = actionBlock } func triggerBlock(control : UIControl) { actionBlock(control) } } private var actionHandlersKey: UInt8 = 0 extension UIControl { var actionHandlers: NSMutableArray { // cat is *effectively* a stored property get { return associatedObject(self, key: &actionHandlersKey, initialiser: { () -> NSMutableArray in return NSMutableArray() }) } set { associateObject(self, key: &actionHandlersKey, value: newValue) } } func addBlockForEvents(events: UIControlEvents, block: ActionBlock) { let actionDelegate = UIButtonActionDelegate(actionBlock: block) actionHandlers.addObject(actionDelegate) // So it gets retained addTarget(actionDelegate, action: #selector(UIButtonActionDelegate.triggerBlock(_:)), forControlEvents: events) } } // MARK: Associated Object wrapper func associatedObject<ValueType: AnyObject>( base: AnyObject, key: UnsafePointer<UInt8>, initialiser: () -> ValueType) -> ValueType { if let associated = objc_getAssociatedObject(base, key) as? ValueType { return associated } let associated = initialiser() objc_setAssociatedObject(base, key, associated, .OBJC_ASSOCIATION_RETAIN) return associated } func associateObject<ValueType: AnyObject>( base: AnyObject, key: UnsafePointer<UInt8>, value: ValueType) { objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN) }