UIButton块相当于addTarget:action:forControlEvents:method?

Ben*_*man 62 iphone uibutton objective-c-blocks

我环顾四周,但在互联网上找不到这个,也没有找到Apple文档中的任何地方,所以我猜它不存在.

但是有一个iOS4块等效API来:

[button addTarget:self action:@selector(tappy:) forControlEvents:UIControlEventTouchUpInside];
Run Code Online (Sandbox Code Playgroud)

我想这可以使用一个类别来实现,但是由于极端的懒惰而宁愿不写这个:)

像这样的东西会很棒:

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

Mar*_*chl 55

我刚刚实现了这个.它的工作就像一个魅力!

它甚至都不难.

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
Run Code Online (Sandbox Code Playgroud)

  • 子类化UIButton是一种反模式,对未来的问题是开放的.它是一个类集群,只有一种方法可以正确地初始化它 - 通过一个永远不会返回子类实例的类方法.它可能现在可以工作,它可能在将来*任何*时间失败. (26认同)
  • 子类化UIButton是一种反模式,因为要安全地执行它,你需要在它的界面中覆盖一切......这很疯狂.在使用UIBarButtonItems的子类时,我遇到了疯狂的事情.一般来说; 不要将类集群子类化.如何安全地做到:(简称)http://www.mikeash.com/pyblog/friday-qa-2010-03-12-subclassing-class-clusters.html再次:(更长更详细)http:/ /cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html (10认同)
  • Eiko,对不起.你在说什么样的反模式?'CallSuper'?据我所知,'Subclassing UIButton'不是反模式. (9认同)
  • @Gabe`UIBarButtonItem不是按钮而是*items*.`UIButton`可以安全地进行子类化,Apple甚至在文档中提到它. (9认同)
  • 有没有办法通过类别对现有按钮执行此操作?我知道问题是有一个实例变量你可以保存块... (4认同)

Cfr*_*Cfr 23

常见的Foundation/UI类增加了一个块库:BlocksKit.这是文档.

它不是UIButton的子类,但添加了UIControl类别:

[button addEventHandler:^(id sender) {
    //do something
} forControlEvents:UIControlEventTouchUpInside];
Run Code Online (Sandbox Code Playgroud)

集合(地图,过滤器等),视图相关的东西等也有块/功能添加.

注意:它与Swift不兼容.

  • 这篇文章向我介绍了cocoapods的精彩世界 (3认同)
  • BlocksKit 2.x中的函数现在是`bk_addEventHandler`.参见[本例](https://gist.github.com/jazzsasori/9051537)作为在BlocksKit 1.x/2.x中使用`addEventHandler`的指南 (2认同)

Sha*_*ney 22

这是一个工作类别实现.在它的当前形式中,这应该仅用于DEBUG.当用户交互和时间很重要时,我将此类与功能(包含在下面)一起用于测试各种代码.同样,这仅用于开发/调试目的,不应考虑用于生产,因此#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
Run Code Online (Sandbox Code Playgroud)

  • 那么这使它不适合生产? (21认同)

lev*_*ese 8

斯威夫特4

这是快速的解决方案

class ClosureSleeve {
let closure: () -> ()

init(attachTo: AnyObject, closure: @escaping () -> ()) {
    self.closure = closure
    objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
}

@objc func invoke() {
    closure()
  }
}

extension UIControl {
func addAction(for controlEvents: UIControlEvents = .primaryActionTriggered, action: @escaping () -> ()) {
    let sleeve = ClosureSleeve(attachTo: self, closure: action)
    addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
 }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

button.addAction {
print("button pressed")
}
Run Code Online (Sandbox Code Playgroud)

  • 您还可以使用 UUID 作为密钥而不是随机数:`UUID().uuidString` (3认同)

lav*_*voy 7

我创建了一个库来做这个!

它支持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");
}];
Run Code Online (Sandbox Code Playgroud)

安装

pod 'ALActionBlocks'
Run Code Online (Sandbox Code Playgroud)


Gab*_*abe 5

我把这个问题彻底解决了,而不是解决这个问题的方法!对UIButton进行子类化创建了一个不值得的雷区.使用Shayne Sweeney的分类(我刚刚通过一系列调整更新了他的答案,以使他的示例生产准备就绪......希望他们能够快速获得批准).

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

如果您只分配UIControlEventTouchUpInside,那么Martin发布的代码应该可以工作......但是有一些问题:

  • 如果调用handleControlEvent,您将使用发布的代码泄漏块:不止一次.
  • 如果您指定了多种类型的事件,它将触发所有事件的最后一个块

在我的代码中,我依赖于块被视为object-c对象,它只适用于iOS4 +(而不是3.2).当我想为按钮状态(即动画)做一些特别的事情时,它对我很有用.您可以使用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
Run Code Online (Sandbox Code Playgroud)