Uibultton كتلة مكافئة لـ AddTarget: الإجراء: forcontrolevents: الطريقة؟

StackOverflow https://stackoverflow.com/questions/3908003

سؤال

نظرت حولي ، لكنني لم أتمكن من العثور على هذا على الإنترنت ، ولا في أي مكان في مستندات Apple ، لذلك أعتقد أنه غير موجود.

ولكن هل هناك كتل 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

نصائح أخرى

هناك مكتبة من الكتل الإضافات إلى فصول الأساس المشترك/واجهة المستخدم: Blockskit. ها هو توثيق.

إنه لا فئة فرعية uibratton ، لكنه يضيف فئة UIControl:

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

هناك أيضًا الكتل/الإضافات الوظيفية للمجموعات (MAP ، مرشح ، إلخ) ، الأشياء ذات الصلة بعروض طرق العرض والمزيد.

ملاحظة: لا يلعب بشكل جيد مع Swift.

إليك تطبيق فئة العمل. في شكله الحالي ، يجب استخدام هذا فقط في 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

لقد قمت بإنشاء مكتبة للقيام بذلك فقط!

انها تؤيد 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'

سريع 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")
}

كتبت هذا منذ فترة طويلة وليس هذا هو السبيل لحل هذه المشكلة !!! تصنيف Uibutton يخلق حقل ألغام لا يستحق ذلك. استخدم فئة Shayne Sweeney (لقد قمت فقط بتحديث إجابته مع مجموعة من التعديلات لجعل مثاله جاهزًا ... آمل أن يتم الموافقة عليه بسرعة).

----- Orig Post -----

يجب أن يعمل الرمز الذي نشره مارتن إذا كنت تقوم فقط بتعيين uicontroleventtouchupinside ... ولكن هناك مشاكل زوجين:

  • سوف تتسرب الكتل باستخدام الرمز المنشور إذا قمت باستدعاء 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

هنالك ريكيت مما يبرز الكتل الكامنة. يمنحك القدرة على إضافة/تجاوز طريقة إلى مثيل باستخدام كتلة.

مع rekit ، يمكنك تحقيق هدف ديناميكي - يستجيب ل button -action - مثل أدناه:

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 لنمط التفويض.

أجد أنه من السهل ومتعدد الاستخدامات استخدام فصل مساعد صغير:

@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];

تنفيذ سويفت التمديد / الفئة القائمة على الفئة التي قمت بتجميعها. استخدام الكائنات المرتبطة بـ OBJC ليس مضادًا للانهيار. : ص

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)
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top