1

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
ItsASecret
  • 2,589
  • 3
  • 19
  • 32
  • You just can't add properties on category (i means on no anonymous category). No ivar can be involved. If you try to synthesize the property (in order to get your setActionBlock: and actionBlock), you will get a compiler error, since it will not be able to create your block ivar. – Mr Bonjour Oct 15 '13 at 14:24

3 Answers3

1

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.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • @ItsASecret: Just for completeness: There *is* a way to add a property in a category, by using so-called "associated objects" instead of instance variables. But a subclass is definitely simpler to implement. – Martin R Oct 15 '13 at 14:46
  • Maybe i'll look into it when I'll have a lot of experience :P Thanks ! – ItsASecret Oct 15 '13 at 14:48
  • Well it turned out to be easy enough with associated references :P I updated my question :) – ItsASecret Oct 15 '13 at 15:40
  • @ItsASecret: Updating the question with the solution makes if difficult for future readers to understand the question, comments and answers. It might be better to *append* new information. – Martin R Oct 15 '13 at 16:10
0

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
Mr Bonjour
  • 3,330
  • 2
  • 23
  • 46
  • Yeah, look carefully at my code, i didn't made a category, i made a subclass of UIRefreshControl. Witch is totally different. Technically, you can't add property to an object at compile time (like you want to do in a category). Because it will change its data structure (and UIRefreshControl is already compiled, you just link it with your code). But you can add a method to an obj because it is bind at runtime. That's why category are made for. There's an exception meanwhile with anonymous category but that only work with your own implementation of code cause compiler can bind all its datastruct – Mr Bonjour Oct 16 '13 at 11:51
  • Oh yeah my mistake it looked really similar ! I was so focused at getting it to work in a category that I didn't see it, sorry :/ I chose to go with the associated reference but thanks ! – ItsASecret Oct 16 '13 at 12:51
0
    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()
Alvin George
  • 14,148
  • 92
  • 64