Pregunta

I really like the way blocks work and thought it would be nice to add them in a few place like setting the action for UIRefreshControl.

So I created a category to UIRefreshControl

@interface UIRefreshControl (Blocks)

@property (nonatomic, copy) void (^actionBlock)();

- (id)initWitActionBlock:(void (^)())actionBlock;

@end

@implementation UIRefreshControl (Blocks)

- (id)initWitActionBlock: (void (^)())actionBlock {
    self = [super init];
    if (self) {
        self.actionBlock = [actionBlock copy];
        [self addTarget:self action:@selector(fireActionBlock) forControlEvents:UIControlEventValueChanged];
    }
    return self;
}

- (void)fireActionBlock {
        self.actionBlock();
}

@end

Which is crashing : reason: '-[UIRefreshControl setActionBlock:]: unrecognized selector sent to instance

But I really don't know blocks that much and also I don't really see the difference between this category and a subclass doing the same thing.

I think I don't fully understand what's happening with properties, so my questions are what should I do ? And if it's possible, is this okay ? Or maybe I shouldn't be doing this ever ?

EDIT : *The solution with associated reference thanks @Martin R!

static char const * const ActionBlockKey = "ActionBlockKey";

@interface UIRefreshControl (Blocks)

@property (nonatomic, copy) void (^actionBlock)();

- (id)initWitActionBlock:(void (^)())actionBlock;

@end

@implementation UIRefreshControl (Blocks)
@dynamic actionBlock;

- (id)initWitActionBlock: (void (^)())actionBlock {
    self = [super init];
    if (self) {
        self.actionBlock = [actionBlock copy];
        [self addTarget:self action:@selector(fireActionBlock) forControlEvents:UIControlEventValueChanged];
    }
    return self;
}

- (void)fireActionBlock {
        self.actionBlock();
}

- (id)actionBlock{
    return objc_getAssociatedObject(self, ActionBlockKey);
}

- (void)setActionBlock:(void (^)())actionBlock{
    objc_setAssociatedObject(self, ActionBlockKey, actionBlock,  OBJC_ASSOCIATION_COPY_NONATOMIC);
}

@end
¿Fue útil?

Solución

The problem is unrelated to blocks.

The compiler does not synthesize properties defined in a class category, because that would require a corresponding instance variable, and you cannot add instance variables in a class category.

Actually you should get a warning like

property 'actionBlock' requires method 'actionBlock' to be defined - use @dynamic or provide a method implementation in this category

I would recommend to create a subclass instead.

Otros consejos

I give you some hint, but haven't tested the code. Note that you should not extend UIKit class like i just did.

@interface MYUIRefreshControl : UIRefreshControl

@property (nonatomic, copy) void (^actionBlock)();

- (id)initWitActionBlock:(void (^)())actionBlock;

@end

@implementation MYUIRefreshControl
@synthesize actionBlock = _actionBlock;

- (id)initWitActionBlock: (void (^)())actionBlock {
    self = [super init];
    if (self) {
        _actionBlock = [actionBlock copy];
        [self addTarget:self action:@selector(fireActionBlock) forControlEvents:UIControlEventValueChanged];
    }
    return self;
}

- (void)fireActionBlock {
      if(_actionBlock)
        _actionBlock();
}

@end
    Try Class Extension. 

    File- New - Swift class - Create one.

    public extension UIRefreshControl
    {
        func makeRefreshView()
        {

            attributedTitle = NSAttributedString(string: "Refreshing ")
            backgroundColor = UIColor.clearColor()
            addTarget(self, action: "refreshnow", forControlEvents: UIControlEvents.ValueChanged)

        }
        func refreshnow()
        {
            print("refreshing and loading")
            endRefreshing()
        }
    }


    Implementation.

let refreshControl = UIRefreshControl()
refreshControl.makeRefreshView()
self.bidderlisttable.addSubview(refreshControl)
refreshControl.refreshnow()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top