Uibultton كتلة مكافئة لـ AddTarget: الإجراء: forcontrolevents: الطريقة؟
-
29-09-2019 - |
سؤال
نظرت حولي ، لكنني لم أتمكن من العثور على هذا على الإنترنت ، ولا في أي مكان في مستندات 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)
}