美文网首页iOS BlogiOS Developer程序员
自定义视图按钮点击事件处理的几种方法

自定义视图按钮点击事件处理的几种方法

作者: Oniityann | 来源:发表于2016-08-17 17:45 被阅读1027次

    一, 关联对象 (runtime)

    其实也是通过block实现, 可以看成一种全局实现
    用类目创建一个UIButton的点击扩展方法, 这种方法的好处就是直接在自定义视图中创建一个button加在视图上即可, 在需要调用的地方调用这个方法, 即可实现button点击事件, 不用每个自定义视图都去写相关方法, 减少代码量. 可能不太好的地方是, 需要创建一个开放的 button 属性, 可能被修改.

    关联对象

    摘自网络:

    关联对象通过来区分, 我们可以把关联对象想成NSDictionary, 把关联对象的值理解为字典中的对象, 存取关联对象上的值就类似于字典中setObject:forKey:方法和objectForKey:方法
    两者的差别是, 关联对象的键是个不透明指针, 所谓不透明指针是其所指向的数据结构不局限于某种特定类型的指针. 在设置关联对象时, 若想让两个键匹配到同一个值, 则二者必须是完全相同的指针, 因此, 使用静态全局变量做 Key 即可

    处理方法:

    以给定的键设置关联对象的值objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
    根据键获取值objc_getAssociatedObject(id object, void *key)
    移除全部关联对象`objc_removeAssociatedObject(id object)

    button类目封装

    首先创建一个类目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 {
        
        // 设置关联对象
        objc_setAssociatedObject(self, buttonClickKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
        
        [self addTarget:self action:@selector(buttonClick:) forControlEvents:event];
    }
    
    - (void)buttonClick:(UIButton *)button {
        
        // 获取关联对象
        ClickActionBlock block = objc_getAssociatedObject(self, buttonClickKey);
        
        if (block) {
            block(button);
        }
    }
    
    @end
    
    

    使用方法

    自定义视图的.h文件中开放相关button属性:

    #import <UIKit/UIKit.h>
    
    @interface JPSUserHeaderView : UIView
    
    @property (nonatomic, weak) UIButton *closeArrow;
    
    @end
    

    自定义视图.m中

    /** close button */
    // 不需要写任何button点击相关方法, 直接添加视图上即可
        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 调用刚才封装的方法
        [headerView.closeArrow setClickBlock:^(UIButton *button) {
        // 需要执行的操作
            [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];
    }
    
    

    二, 在自定义视图中单独绑定block

    这种方法仅适用于当前自定义视图, 如果项目中用的全是这个自定义视图, 那么可以直接这样写, 不用开放相关属性给外界, 更安全

    写法

    自定义视图.h中:

    #import <UIKit/UIKit.h>
    
    // typedef block块直接创建一个block, 不定义属性是嫌麻烦, 太懒= =
    typedef void(^AvatarButtonBlock)(UIButton *button);
    
    @interface JPSLearnHomeHeaderView : UIView
    
    // 第二种调用形式, 可以写在私有属性或者公有
    //@property (nonatomic, copy) AvatarButtonBlock block;
    
    // 设置回调
    - (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;
        
        // 不用于关联对象, 单独绑定需要给按钮添加target/action来绑定block
        [avatar addTarget:self action:@selector(modalToUserPage:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:avatar];
    
    // 按钮点击事件
    - (void)modalToUserPage:(UIButton *)button {
        if (self.clickEvent) {
            self.clickEvent(button);
        }
    }
    
    // block回调实现
    - (void)setAvatarClick:(AvatarButtonBlock)block {
        if (_clickEvent != block) {
            self.clickEvent = block;
        }
    }
    

    使用

    添加自定义视图的控制器中:

    /** header view */
        JPSLearnHomeHeaderView *headerView = [[JPSLearnHomeHeaderView alloc] initWithFrame:HeaderViewF];
        
        // 调用方法一:
        // 用自定义视图调用 block 回调
        [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属性是开放的, 可以直接调用
        headerView.block = ^(UIButton *button) {
           // 需要实现的代码   
           JPSUserTableController *userController = [[JPSUserTableController alloc] init];
            
           JPSNavController *nav = [[JPSNavController alloc] initWithRootViewController:userController];
            
           [self presentViewController:nav animated:YES completion:nil];
        };
    #endif
        
        [self.view addSubview:headerView];
    

    其他实现方法

    • 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;
    }
    
    • 代理
      代理步骤太多了, 感觉仅处理简单事件感觉完全没必要...
      不过代理处理业务逻辑比较复杂的时候很好用.

    相关文章

      网友评论

      • 7b5fe96cf0e9:楼主是好人
      • doubleJJ:请问第二个方法中,self.clickEvent是什么啊
      • b9b69a12a7ec:第一个方法里,为什么blcok不会回调。。 我几乎全复制你的。
        Oniityann:@小凡啊小凡 不可能啊,我项目里面很多关联对象,这个用的也好好的,你检查下是不是key写错了
      • 施主小欣:膜拜楼主!

      本文标题:自定义视图按钮点击事件处理的几种方法

      本文链接:https://www.haomeiwen.com/subject/pfgmsttx.html