iu

background

After upgrading to Xcode9, the project was in trouble and crashed whenever it encountered RAC.






After trying all sorts of things, I decided to remove RAC from the project. RAC was only a secondary part of my project, mainly to simplify the code, so I didn’t use it very much.

After removing RAC, it is necessary to find something to replace RAC. I consulted the high-end female dress makers of iOS and they recommended BlocksKit to me, which is the twin of RAC and is so good. I completed the RAC to BlocksKit replacement in minutes, but to my surprise, I still crashed when running the BlocksKit code…





I really can’t take it

Problems encountered

Since I am not allowed to use three parties, it seems that I have to do it myself. Let’s look at a similar code:

+ (void)showAlertWithType:(NSInteger)type buttonClickedBlock:(void (^)(void))buttonClickedBlock { UIWindow *delegateWindow = [[[UIApplication sharedApplication] delegate] window]; UIView *bgView = [[UIView alloc] initWithFrame:CGRectMake(90, 90, 200, 200)]; [delegateWindow addSubview:bgView]; bgView.backgroundColor = [UIColor grayColor]; / / button (click on the button gray view into red) UIButton * button = [UIButton buttonWithType: UIButtonTypeSystem]; [bgView addSubview:button]; button.frame = CGRectMake(30, 30, 90, 40); [button setTitle: @ "click on the red" forState: UIControlStateNormal]; // How to click the button to turn the grey view red and execute the block? // Button addTarget:<#(nullable ID)#> Action :<#(nonnull SEL)#> forControlEvents:<#(UIControlEvents)#>}Copy the code

This is a class method that calls back the block when clicked, which is too easy to implement using RAC or BlocksKit:

[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
    if (buttonClickedBlock) {
        buttonClickedBlock();
    }
}];
Copy the code

But what if you’re not allowed to use RAC or BlocksKit?





To solve the problem

The perfect way to solve this problem is to implement a feature like RAC or BlocksKit in your own way. When I was worried about this, my master came to report this morning (it was a pity that he did not write simple books) and shared a custom button written by him with me. After reading my master’s button, I got inspired and wrote a category on this basis:

Make your UIButton come with a block

#import "UIButton+CQBlock.h" #import <objc/runtime.h> typedef void(^CQ_ButtonEventsBlock)(void); @interface UIButton () /** Event callback block */ @property (nonatomic, copy) CQ_ButtonEventsBlock CQ_ButtonEventsBlock; @end@implementation UIButton (CQBlock) //------- add attribute -------// static void *cq_buttonEventsBlockKey = &cq_buttonEventsBlockKey; - (CQ_ButtonEventsBlock)cq_buttonEventsBlock { return objc_getAssociatedObject(self, &cq_buttonEventsBlockKey); } - (void)setCq_buttonEventsBlock:(CQ_ButtonEventsBlock)cq_buttonEventsBlock { objc_setAssociatedObject(self, &cq_buttonEventsBlockKey, cq_buttonEventsBlock, OBJC_ASSOCIATION_COPY); } /** bind the button event callback block. @param block callback block. @param controlEvents callback block event */ - (void)cq_addEventHandler:(void) (^)(void))block forControlEvents:(UIControlEvents)controlEvents { self.cq_buttonEventsBlock = block; [self addTarget:self action:@selector(cq_blcokButtonClicked) forControlEvents:controlEvents]; Cq_blcokButtonClicked {if (self.cq_buttonEventsBlock) {self.cq_buttonEventsblock (); } } @endCopy the code

Use:

[button cq_addEventHandler: ^ {NSLog (@ "click on the button");} forControlEvents: UIControlEventTouchUpInside];Copy the code

Method name I am mimicking BlocksKit:

[button bk_addEventHandler: ^ (id sender) {NSLog (@ "click on the button");} forControlEvents: UIControlEventTouchUpInside];Copy the code

Then all buttons have blocks, which make them feel the same as BlocksKit. This replaces RAC with a minor change.





Pretty strong, I have to say

extension

Once you’ve learned this routine, it’s easy to implement similar requirements. For example, BlocksKit has a method:

[self.view bk_whenTapped:^{NSLog(@" click ");}];Copy the code

I wrote a copy of it:

[self.view cq_whenTapped:^{NSLog(@" click ");}];Copy the code

Detailed code:

#import "UIView+CQBlock.h" #import <objc/runtime.h> typedef void(^CQ_ViewTappedBlock)(void); @interface UIView () /** Click the block of the gesture event callback */ @property (nonatomic, copy) CQ_ViewTappedBlock CQ_ViewTappedBlock; @end@implementation UIView (CQBlock) //------- add attribute -------// static void *cq_viewTappedBlockKey = &cq_viewTappedBlockKey; - (CQ_ViewTappedBlock)cq_viewTappedBlock { return objc_getAssociatedObject(self, &cq_viewTappedBlockKey); } - (void)setCq_viewTappedBlock:(CQ_ViewTappedBlock)cq_viewTappedBlock { objc_setAssociatedObject(self, &cq_viewTappedBlockKey, cq_viewTappedBlock, OBJC_ASSOCIATION_COPY); } /** tappedBlock block @param tappedBlock {- (void)cq_whenTapped:(void(^)(void))tappedBlock { self.cq_viewTappedBlock = tappedBlock; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(viewTapped)]; [self addGestureRecognizer:tapGesture]; } // Click view - (void)viewTapped {if (self.cq_viewtappedbLock) {self.cq_viewtappedbLock (); // Click view - (void)viewTapped {if (self.cq_viewtappedblock) {self.cq_viewtappedbLock (); }}Copy the code

It’s essentially adding a block property to UIView using category and runtime, and calling back that block when you click on it.

Here is the demo

Credit:

Thank you for your sharing this morning.

conclusion

Happy is the child who has a master!





The victorious Awwww. GIF

Updated on October 19, 2017

There is a simplified way to call a block:

if (self.cq_viewTappedBlock) {
    self.cq_viewTappedBlock();
}
Copy the code

Is equivalent to:

! self.cq_viewTappedBlock ? : self.cq_viewTappedBlock();Copy the code