一, 關(guān)聯(lián)對象 (runtime)
其實(shí)也是通過block實(shí)現(xiàn), 可以看成一種全局實(shí)現(xiàn)
用類目創(chuàng)建一個(gè)UIButton的點(diǎn)擊擴(kuò)展方法, 這種方法的好處就是直接在自定義視圖中創(chuàng)建一個(gè)button加在視圖上即可, 在需要調(diào)用的地方調(diào)用這個(gè)方法, 即可實(shí)現(xiàn)button點(diǎn)擊事件, 不用每個(gè)自定義視圖都去寫相關(guān)方法, 減少代碼量. 可能不太好的地方是, 需要?jiǎng)?chuàng)建一個(gè)開放的 button 屬性, 可能被修改.
關(guān)聯(lián)對象
摘自網(wǎng)絡(luò):
關(guān)聯(lián)對象通過鍵來區(qū)分, 我們可以把關(guān)聯(lián)對象想成
NSDictionary, 把關(guān)聯(lián)對象的值理解為字典中的對象, 存取關(guān)聯(lián)對象上的值就類似于字典中setObject:forKey:方法和objectForKey:方法
兩者的差別是, 關(guān)聯(lián)對象的鍵是個(gè)不透明指針, 所謂不透明指針是其所指向的數(shù)據(jù)結(jié)構(gòu)不局限于某種特定類型的指針. 在設(shè)置關(guān)聯(lián)對象時(shí), 若想讓兩個(gè)鍵匹配到同一個(gè)值, 則二者必須是完全相同的指針, 因此, 使用靜態(tài)全局變量做 Key 即可
處理方法:
以給定的鍵設(shè)置關(guān)聯(lián)對象的值
objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
根據(jù)鍵獲取值objc_getAssociatedObject(id object, void *key)
移除全部關(guān)聯(lián)對象`objc_removeAssociatedObject(id object)
button類目封裝
首先創(chuàng)建一個(gè)類目UIButton + XXX(XXX隨你便)
在XXX.h中:
#import <UIKit/UIKit.h>
typedef void(^ClickActionBlock)(UIButton *button);
@interface UIButton (ZYNBlock)
- (void)setClickBlock:(ClickActionBlock)block andEvent:(UIControlEvents)event;
@end
在XXX.M中
#import "UIButton+ZYNBlock.h"
//別忘記引用
#import <objc/runtime.h>
static void *buttonClickKey = "buttonClickKey";
@implementation UIButton (ZYNBlock)
- (void)setClickBlock:(ClickActionBlock)block andEvent:(UIControlEvents)event {
// 設(shè)置關(guān)聯(lián)對象
objc_setAssociatedObject(self, buttonClickKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self addTarget:self action:@selector(buttonClick:) forControlEvents:event];
}
- (void)buttonClick:(UIButton *)button {
// 獲取關(guān)聯(lián)對象
ClickActionBlock block = objc_getAssociatedObject(self, buttonClickKey);
if (block) {
block(button);
}
}
@end
使用方法
自定義視圖的.h文件中開放相關(guān)button屬性:
#import <UIKit/UIKit.h>
@interface JPSUserHeaderView : UIView
@property (nonatomic, weak) UIButton *closeArrow;
@end
自定義視圖.m中
/** close button */
// 不需要寫任何button點(diǎn)擊相關(guān)方法, 直接添加視圖上即可
UIButton *closeArrow = [UIButton buttonWithType:UIButtonTypeCustom];
[closeArrow setImage:[UIImage imageNamed:@"user_close"] forState:UIControlStateNormal];
closeArrow.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:closeArrow];
self.closeArrow = closeArrow;
在添加自定義視圖的控制器中:
- (void)setupSubViews {
JPSUserHeaderView *headerView = [[JPSUserHeaderView alloc] initWithFrame:HeaderViewF];
// 直接用自定義視圖里面的 button 調(diào)用剛才封裝的方法
[headerView.closeArrow setClickBlock:^(UIButton *button) {
// 需要執(zhí)行的操作
[self dismissViewControllerAnimated:YES completion:nil];
} andEvent:UIControlEventTouchUpInside];
self.tableView.tableHeaderView = headerView;
self.tableView.contentInset = UIEdgeInsetsMake(-20, 0, 0, 0);
self.tableView.showsVerticalScrollIndicator = NO;
self.tableView.showsHorizontalScrollIndicator = NO;
self.tableView.alwaysBounceVertical = NO;
self.tableView.backgroundColor = [UIColor colorWithRed:0.908 green:0.926 blue:0.932 alpha:1.000];
[self.tableView registerClass:[JPSTableViewCell class] forCellReuseIdentifier:cellIdentifier];
}
二, 在自定義視圖中單獨(dú)綁定block
這種方法僅適用于當(dāng)前自定義視圖, 如果項(xiàng)目中用的全是這個(gè)自定義視圖, 那么可以直接這樣寫, 不用開放相關(guān)屬性給外界, 更安全
寫法
自定義視圖.h中:
#import <UIKit/UIKit.h>
// typedef block塊直接創(chuàng)建一個(gè)block, 不定義屬性是嫌麻煩, 太懶= =
typedef void(^AvatarButtonBlock)(UIButton *button);
@interface JPSLearnHomeHeaderView : UIView
// 第二種調(diào)用形式, 可以寫在私有屬性或者公有
//@property (nonatomic, copy) AvatarButtonBlock block;
// 設(shè)置回調(diào)
- (void)setAvatarClick:(AvatarButtonBlock)block;
@end
自定義視圖.m中
/** avatar */
UIButton *avatar = [UIButton buttonWithType:UIButtonTypeCustom];
[avatar setBackgroundImage:[UIImage imageNamed:@"user_avatar"] forState:UIControlStateNormal];
avatar.layer.masksToBounds = YES;
avatar.layer.cornerRadius = 12;
// 不用于關(guān)聯(lián)對象, 單獨(dú)綁定需要給按鈕添加target/action來綁定block
[avatar addTarget:self action:@selector(modalToUserPage:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:avatar];
// 按鈕點(diǎn)擊事件
- (void)modalToUserPage:(UIButton *)button {
if (self.clickEvent) {
self.clickEvent(button);
}
}
// block回調(diào)實(shí)現(xiàn)
- (void)setAvatarClick:(AvatarButtonBlock)block {
if (_clickEvent != block) {
self.clickEvent = block;
}
}
使用
添加自定義視圖的控制器中:
/** header view */
JPSLearnHomeHeaderView *headerView = [[JPSLearnHomeHeaderView alloc] initWithFrame:HeaderViewF];
// 調(diào)用方法一:
// 用自定義視圖調(diào)用 block 回調(diào)
[headerView setAvatarClick:^(UIButton *button) {
JPSUserTableController *userController = [[JPSUserTableController alloc] init];
JPSNavController *nav = [[JPSNavController alloc] initWithRootViewController:userController];
[self presentViewController:nav animated:YES completion:nil];
}];
#if 0
// 如果自定義view里面的block屬性是開放的, 可以直接調(diào)用
headerView.block = ^(UIButton *button) {
// 需要實(shí)現(xiàn)的代碼
JPSUserTableController *userController = [[JPSUserTableController alloc] init];
JPSNavController *nav = [[JPSNavController alloc] initWithRootViewController:userController];
[self presentViewController:nav animated:YES completion:nil];
};
#endif
[self.view addSubview:headerView];
其他實(shí)現(xiàn)方法
-
tableview data source代理方法中:(不推薦, 這樣寫太丑)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
[cell.button addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
- 代理
代理步驟太多了, 感覺僅處理簡單事件感覺完全沒必要...
不過代理處理業(yè)務(wù)邏輯比較復(fù)雜的時(shí)候很好用.