63

周りを見回しましたが、インターネットでもAppleのドキュメントでも見つからなかったので、存在しないと思います。

しかし、iOS4ブロックと同等のAPIはありますか?

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

これはカテゴリを使用して実装できると思いますが、極端な怠惰のために自分でこれを書くのはやめたほうがいいです:)

このようなものは素晴らしいでしょう:

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

10 に答える 10

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
于 2010-10-20T11:33:39.557 に答える
24

一般的なFoundation/UIクラスに追加されたブロックのライブラリがあります:BlocksKit。これがドキュメントです。

UIButtonをサブクラス化しませんが、UIControlカテゴリを追加します。

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

コレクション(マップ、フィルターなど)、ビュー関連のものなどへのブロック/機能の追加もあります。

注:Swiftではうまく機能しません。

于 2012-10-31T08:09:22.237 に答える
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
于 2011-03-23T18:23:49.927 に答える
12

スウィフト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)
    }
}

使用例:

button.addAction {
    print("button pressed")
}
于 2018-06-20T05:54:01.250 に答える
7

これを行うためのライブラリを作成しました。

UIControlUIButton) 、、、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'
于 2013-11-05T17:44:35.570 に答える
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
于 2010-12-04T00:00:28.360 に答える
1

ブロック潜在能力を引き出すREKitがあります。Blockを使用してインスタンスにメソッドを追加/オーバーライドする機能を提供します。

REKitを使用すると、以下のように、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];

サブクラスやカテゴリを作成する必要はありません。

ターゲット/アクションパラダイムに加えて、委任パターンにREKitを使用できます。

于 2013-02-14T15:40:01.933 に答える
1

私が作成した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)
}
于 2016-08-09T03:37:53.613 に答える
1

小さなヘルパークラスを使用するのは簡単で用途が広いと思います。

@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];
于 2016-11-06T14:04:42.683 に答える
0

より単純な解決策(拡張機能のないものなど):

void (^eventHandlerBlock)(void) = ^{
    printf("\nHandling event for button %lu\n", some_local_variable);
};
objc_setAssociatedObject(button, @selector(invoke), eventHandlerBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[button addTarget:eventHandlerBlock action:@selector(invoke) forControlEvents:UIControlEventAllEvents];

ブロックの代わりにセレクターを使用する場合は、次の点に注意してください。

  1. ブロック変数を宣言する場合は、;staticを省略できます。objc_setAssociatedObjectただし、コンパイル以外の定数(など)は使用できませんsome_local_variable
  2. void(^)(void)で置き換えることができますdispatch_block_t
于 2021-11-18T06:30:01.613 に答える