block使用浅析

作者: Z了个L | 来源:发表于2016-02-20 19:53 被阅读146次
    • 实现目标,给键盘添加一个工具条

    LZKeyboardTool.xib图:

    // 自定义工具条
    // LZKeyboardTool.h
    #import <UIKit/UIKit.h>
    
    typedef enum {
        KeyboardItemTypePrevious, // 上一个
        KeyboardItemTypeNext, // 下一个
        KeyboardItemTypeDone // 完成
    } KeyboardItemType;
    
    // 定义一个类型
    typedef void (^myBlock)(KeyboardItemType);
    
    @interface LZKeyboardTool : UIView
    
    + (instancetype)sharekeyboardTool;
    
    @property (nonatomic, copy) myBlock pBlock;
    
    @end
    
    // LZKeyboardTool.m
    #import "LZKeyboardTool.h"
    
    @interface LZKeyboardTool()
    
    @end
    
    @implementation LZKeyboardTool
    
    static LZKeyboardTool* _instance;
    
    + (void)load
    {
        // 创建一个对象
        _instance = [[[NSBundle mainBundle] loadNibNamed:@"LZKeyboardTool" owner:nil options:nil] lastObject];
    
    }
    // 单例对象
    + (instancetype)sharekeyboardTool
    {
        return _instance;
    }
    
    // 上一个
    - (IBAction)previous:(id)sender {
        if (_pBlock) { // 先判断
            _pBlock(KeyboardItemTypePrevious); // 调用block
        }
    }
    
    // 下一个
    - (IBAction)next:(id)sender {
        if (_pBlock) { // 先判断
            _pBlock(KeyboardItemTypeNext); // 调用block
        }
    }
    // 完成
    - (IBAction)done:(id)sender {
        if (_pBlock) { // 先判断
            _pBlock(KeyboardItemTypeDone); // 调用block
        }
    }
    
    @end
    
    
    // ViewController.h
    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController
    
    
    @end
    
    
    // ViewController.m
    #import "ViewController.h"
    #import "LZKeyboardTool.h"
    
    @interface ViewController ()
    {
        NSArray *_fields; // 存储所有的textField
    }
    
    // 生日框
    @property (weak, nonatomic) IBOutlet UITextField *birthdayField;
    // 输入框容器
    @property (weak, nonatomic) IBOutlet UIView *inputContainer;
    /** LZKeyboard数据*/
    @property (nonatomic, strong) LZKeyboardTool *tool;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 1.初始化自定义键盘
        [self setupCustomKeyboard];
    
        // 创建自定义键盘
        self.tool = [LZKeyboardTool sharekeyboardTool];
    
        // 2.设置每一个textfield的键盘工具view(inputAccessoryView)
        [self setupKeyboardTool];
    
        // 3.监听键盘的事件
        [self setupKeyboardNotification];
    
        // 含义,弱引用,防止循环引用
        // #define WeakSelf __weak typeof(self) weakSelf = self;
        __weak typeof(self) weakSelf = self;
    
        // 用block保存一段代码
        self.tool.pBlock = ^ (KeyboardItemType itemType){
            // 获取当前响应者的索引
            int currentIndex = [weakSelf getCurrentResponderIndex];
    
            switch (itemType) {
                case KeyboardItemTypePrevious:
                    NSLog(@"上一个");
                    [weakSelf showPreviousField:currentIndex];
                    break;
                case KeyboardItemTypeNext:
                    [weakSelf showNextField:currentIndex];
                    break;
                case KeyboardItemTypeDone:
                    [weakSelf touchesBegan:nil withEvent:nil];
                    break;
            }
    
        };
    
    }
    
    // 获取当前textField的响应者索引
    // 如果返回-1代理没有找到响应者
    - (int)getCurrentResponderIndex
    {
        // 遍历所有的textField获取响应者
        for (UITextField *tf in _fields) {
            if (tf.isFirstResponder) {
                return [_fields indexOfObject:tf];
            }
        }
        return -1;
    }
    
    // 1.初始化自定义键盘
    - (void)setupCustomKeyboard
    {
        UIDatePicker *datePicker = [[UIDatePicker alloc] init];
    
        datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"zh"];
        datePicker.datePickerMode = UIDatePickerModeDate;
    
        self.birthdayField.inputView = datePicker;
    }
    
    // 2.设置每一个textfield的键盘工具view(inputAccessoryView)
    - (void)setupKeyboardTool
    {
        // 创建工具栏
        LZKeyboardTool *tool = [LZKeyboardTool sharekeyboardTool];
    
        // 1.获取输入框窗口的所有子控件
        NSArray *views = self.inputContainer.subviews;
    
        // 创建一个数据存储textfield
        NSMutableArray *fieldsM = [NSMutableArray array];
    
        // 2.遍历
        for (UIView *child in views) {
            // 如果子控制器是UITextField的时候,设置inputAccessoryView
            if ([child isKindOfClass:[UITextField class]]) {
                UITextField *tf = (UITextField *)child; // 类型转换
                tf.inputAccessoryView = tool;
                [fieldsM addObject:tf];
            }
        }
    
        _fields = fieldsM;
    
    }
    
    - (void)setupKeyboardNotification
    {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kbFrameChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
    }
    
    - (void)kbFrameChange:(NSNotification *)notifi
    {
    //    NSLog(@"%@", notifi);
    //    NSLog(@"%@", notifi.userInfo[@"UIKeyboardFrameEndUserInfoKey"]);
        // 获取键盘改变的y值
        // 键盘结束时的fm
        CGRect kbEndFrm = [notifi.userInfo[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
    
        // 键盘结束时的y
        CGFloat kEndY = kbEndFrm.origin.y;
    
        // 获取当前的响应者
        int currentIndex = [self getCurrentResponderIndex];
        UITextField *currentTf = _fields[currentIndex];
        int inputY = self.inputContainer.frame.origin.y;
        CGFloat tfMaxY = CGRectGetMaxY(currentTf.frame) + inputY;
        NSLog(@"kEndY = %f, tfMaxY = %f, inputY = %d", kEndY, tfMaxY, inputY);
        // 改变控制器view的transform
        if (tfMaxY > kEndY) {
            self.view.transform = CGAffineTransformMakeTranslation(0, kEndY - tfMaxY);
        }else{
            [UIView animateWithDuration:0.25 animations:^{
                self.view.transform = CGAffineTransformIdentity; // 恢复到原来位置
            }];
        }
    
    }
    
    #pragma mark -键盘工具条的代理
    
    // 让上一个field成为响应者
    - (void)showPreviousField:(int) currentIndex{
        int previousIndex = currentIndex - 1;
        if (previousIndex >= 0) {
            UITextField *previousTf = [_fields objectAtIndex:previousIndex];
            [previousTf becomeFirstResponder];
        }
    }
    
    - (void)showNextField:(int) currentIndex{
        int nextIndex = currentIndex + 1;
        if (nextIndex < _fields.count) {
            UITextField *nextTf = [_fields objectAtIndex:nextIndex];
            [nextTf becomeFirstResponder];
        }
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        [self.view endEditing:YES];
    
        [UIView animateWithDuration:0.25 animations:^{
            self.view.transform = CGAffineTransformIdentity; // 恢复到原来位置
        }];
    }
    
    @end
    
    

    效果图片:

    • 笔者主要是想通过该示例程序来说明一下笔者使用block的步骤 - 在LZKeyboardTool.h里面定义一个block类型,并且定义了一个pBlock变量

      // 定义一个类型
      typedef void (^myBlock)(KeyboardItemType);
      
      @interface LZKeyboardTool : UIView
      
      + (instancetype)sharekeyboardTool;
      
      @property (nonatomic, copy) myBlock pBlock;
      
      @end
      
      • 在ViewController.m里面使用pBlock变量保存了一段代码,里面比如把用到的self(指代的是控制器)重新定义了一个弱指针指向了它,为了防止在pBlock保存的代码中使用到self而造成循环引用,这里笔者也是网上百度搜索到的,这里也恰好是重点
      // 含义,弱引用,防止循环引用
      __weak typeof(self) weakSelf = self;
      
      // 用block保存一段代码
      self.tool.pBlock = ^ (KeyboardItemType itemType){
          // 获取当前响应者的索引
          int currentIndex = [weakSelf getCurrentResponderIndex];
      
          switch (itemType) {
              case KeyboardItemTypePrevious:
                  NSLog(@"上一个");
                  [weakSelf showPreviousField:currentIndex];
                  break;
              case KeyboardItemTypeNext:
                  [weakSelf showNextField:currentIndex];
                  break;
              case KeyboardItemTypeDone:
                  [weakSelf touchesBegan:nil withEvent:nil];
                  break;
          }
      
      };
      
      • 在LZKeyboardTool.m里面调用pBlock保存的代码
      // 上一个
      -(IBAction)previous:(id)sender {
      if (_pBlock) { // 先判断
          _pBlock(KeyboardItemTypePrevious); // 调用block
      }
      }
      
      • OK,结束,就这么简单
    • 下面在来一个示例代码,不是全部的,主要是为了说明block怎么去使用,首先通过下面这张图片来表达笔者的意图,点击加号按钮或者减号按钮,得控制下面显示的总价格,第一,加号按钮和减号按钮位于tableView上,下面那一个UIView是与tableView分隔开的,也就是他俩不能进行数据的交互,要实现两者之间交互,可以通过 通知,代理, KVO, block,这里笔者就使用了block来实现,主要是为了说明block的用法


    // tableView的cell模型

    // XMGWineCell.h
    #import <UIKit/UIKit.h>
    @class XMGWine;
    
    typedef enum {
        WineCellTypePlus,
        WineCellTypeMinus
    } WineCellType;
    
    // 定义一个类型
    typedef void (^myBlock)(WineCellType);
    
    @interface XMGWineCell : UITableViewCell
    
    /** 酒模型*/
    @property (nonatomic, strong) XMGWine *wine;
    
    @property (nonatomic, copy) myBlock pBlock;
    
    @end
    
    // XMGWineCell.m
    #import "XMGWineCell.h"
    #import "XMGWine.h"
    #import "XMGCircleButton.h"
    
    @interface XMGWineCell ()
    
    @property (weak, nonatomic) IBOutlet UIImageView *imageImageView;
    @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
    @property (weak, nonatomic) IBOutlet UILabel *moneyLabel;
    @property (weak, nonatomic) IBOutlet XMGCircleButton *minusButton;
    @property (weak, nonatomic) IBOutlet UILabel *countLabel;
    
    
    @end
    
    @implementation XMGWineCell
    
    - (void)setWine:(XMGWine *)wine
    {
        _wine = wine;
        self.imageImageView.image = [UIImage imageNamed:wine.image];
        self.nameLabel.text = wine.name;
        self.moneyLabel.text = [NSString stringWithFormat:@"¥%@", wine.money];
    
        self.countLabel.text = [NSString stringWithFormat:@"%zd", wine.count];
    //    self.minusButton.enabled = (wine.count > 0);
    }
    
    /**
     *  -
     */
    - (IBAction)minusClick {
        // 设置模型数据-1
        self.wine.count--;
        // 判断r
        if (self.wine.count == 0) { // 减号不能点击
            self.minusButton.enabled = NO;
        }
        // 赋值
        self.countLabel.text = [NSString stringWithFormat:@"%zd", self.wine.count];
    
        if (_pBlock) { // 判断
            _pBlock(WineCellTypeMinus); // 调用block
        }
    }
    /**
     *  +
     */
    - (IBAction)plusClick {
        // 设置减号按钮可以点击
        self.minusButton.enabled = YES;
        // 设置模型数据加1
        self.wine.count++;
        // 刷新
        self.countLabel.text = [NSString stringWithFormat:@"%zd", self.wine.count];
    
        if (_pBlock) { // 判断
            _pBlock(WineCellTypePlus); // 调用block
        }
    }
    
    
    @end
    
    
    // ViewController.h
    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController
    
    
    @end
    
    // ViewController.m
    #import "ViewController.h"
    #import "XMGWineCell.h"
    #import "MJExtension.h"
    #import "XMGWine.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    /**
     *  购买按钮
     */
    @property (weak, nonatomic) IBOutlet UIButton *buyCount;
    
    @property (weak, nonatomic) IBOutlet UITableView *tableView;
    /** 酒模型数组*/
    @property (nonatomic, strong) NSArray *wineArray;
    /** 总价格*/
    @property (weak, nonatomic) IBOutlet UILabel *totalPriceLabel;
    
    /** 购物车对象(存放需要购买的商品) */
    @property (nonatomic, strong) NSMutableArray *wineCar;
    @end
    
    @implementation ViewController
    
    #pragma mark - 懒加载数据
    
    - (NSMutableArray *)wineCar
    {
        if (!_wineCar) {
            _wineCar = [NSMutableArray array];
        }
        return _wineCar;
    }
    
    - (NSArray *)wineArray
    {
        if (_wineArray == nil) {
            _wineArray = [XMGWine mj_objectArrayWithFilename:@"wine.plist"];
        }
        return _wineArray;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
    }
    
    #pragma mark - XMGWineCellDelegate 方法
    - (void)wineCellDidClickPlusButton:(XMGWineCell *)wineCell
    {
        // 计算总价
        int totalPrice = self.totalPriceLabel.text.intValue + wineCell.wine.money.intValue;
    
        // 设置总价
        self.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];
    
        self.buyCount.enabled = YES;
    
        // 如果这个商品已经在购物车中,就不用再添加
        if ([self.wineCar containsObject:wineCell.wine]) return;
    
        // 添加需要购买的商品
        [self.wineCar addObject:wineCell.wine];
    }
    
    - (void)wineCellDidClickMinusButton:(XMGWineCell *)wineCell
    {
        // 计算总价
        int totalPrice = self.totalPriceLabel.text.intValue - wineCell.wine.money.intValue;
    
        // 设置总价
        self.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];
    
        self.buyCount.enabled = totalPrice > 0;
    
        // 将商品从购物车中移除
        if (wineCell.wine.count == 0) {
            [self.wineCar removeObject:wineCell.wine];
        }
    }
    
    /**
     *  购买
     */
    - (IBAction)buy{
        // 循环
        for (XMGWine *wine in self.wineArray) {
            if (wine.count) { // 判断
                NSLog(@"[%@]卖了%zd瓶", wine.name, wine.count);
            }
        }
    }
    
    /**
     *  清空
     */
    - (IBAction)clear {
        // 模型数据
        for (XMGWine *wine in self.wineArray) {
            wine.count = 0;
        }
        // 刷新数据
        [self.tableView reloadData];
        // 设置购买按钮不可点击
        self.buyCount.enabled = NO;
        // 设置总价格totalPriceLabel值为0
        self.totalPriceLabel.text = @"0";
    }
    
    
    #pragma mark - UITableViewDataSource方法
    /**
     * 每组多少行
     */
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.wineArray.count;
    }
    
    /**
     *  每行显示什么内容
     */
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // 去缓存里面找
        // 含义,弱引用,防止循环引用
        __weak typeof(self) weakSelf = self;
    
        static NSString *ID = @"wine";
        // 如果在block中访问了外界对象,那么就得加上__block修饰符
        __block __weak XMGWineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        // 赋值
        XMGWine *wine = self.wineArray[indexPath.row];
        cell.wine = wine;
        // 设置
        cell.pBlock = ^ (WineCellType itemType){
            int totalPrice = 0;
            switch (itemType) {
                case WineCellTypePlus:
                    // 计算总价
                    totalPrice = weakSelf.totalPriceLabel.text.intValue + cell.wine.money.intValue;
    
                    // 设置总价
                    weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];
    
                    weakSelf.buyCount.enabled = YES;
                    // 如果这个商品已经在购物车中,就不用再添加
                    if ([weakSelf.wineCar containsObject:cell.wine]) return;
                    // 添加需要购买的商品
                    [weakSelf.wineCar addObject:cell.wine];
    
                    break;
    
                case WineCellTypeMinus:
                    // 计算总价
                    totalPrice = weakSelf.totalPriceLabel.text.intValue - cell.wine.money.intValue;
    
                    // 设置总价
                    weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];
    
                    weakSelf.buyCount.enabled = totalPrice > 0;
    
                    // 将商品从购物车中移除
                    if (cell.wine.count == 0) {
                        [weakSelf.wineCar removeObject:cell.wine];
                    }
    
                    break;
            }
        };
        // 返回cell
        return cell;
    }
    
    @end
    
    
    
    • 下面笔者来说明一下使用block的步骤

      • 在XMGWineCell.h里面定义一个block类型,并且定义一个pBlock变量
      // 定义一个类型
      typedef void (^myBlock)(WineCellType);
      
      @interface XMGWineCell : UITableViewCell
      
      /** 酒模型*/
      @property (nonatomic, strong) XMGWine *wine;
      
      @property (nonatomic, copy) myBlock pBlock;
      
      • 在ViewController.m里面通过pBlock变量来保存一段代码,这句代码__weak typeof(self) weakSelf = self;笔者就不多解释了,主要是这句代码__block __weak XMGWineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];很关键,因为pBlock变量保存的代码中用到了外界对象,外界对象得加上__block修饰符才能防止循环引用,就这么简单
      // 去缓存里面找
      // 含义,弱引用,防止循环引用
      __weak typeof(self) weakSelf = self;
      
      static NSString *ID = @"wine";
      // 如果在block中访问了外界对象,那么就得加上__block修饰符
      __block __weak XMGWineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
      // 赋值
      XMGWine *wine = self.wineArray[indexPath.row];
      cell.wine = wine;
      // 设置
      cell.pBlock = ^ (WineCellType itemType){
          int totalPrice = 0;
          switch (itemType) {
              case WineCellTypePlus:
                  // 计算总价
                  totalPrice = weakSelf.totalPriceLabel.text.intValue + cell.wine.money.intValue;
      
                  // 设置总价
                  weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];
      
                  weakSelf.buyCount.enabled = YES;
                  // 如果这个商品已经在购物车中,就不用再添加
                  if ([weakSelf.wineCar containsObject:cell.wine]) return;
                  // 添加需要购买的商品
                  [weakSelf.wineCar addObject:cell.wine];
      
                  break;
      
              case WineCellTypeMinus:
                  // 计算总价
                  totalPrice = weakSelf.totalPriceLabel.text.intValue - cell.wine.money.intValue;
      
                  // 设置总价
                  weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];
      
                  weakSelf.buyCount.enabled = totalPrice > 0;
      
                  // 将商品从购物车中移除
                  if (cell.wine.count == 0) {
                      [weakSelf.wineCar removeObject:cell.wine];
                  }
      
                  break;
          }
      };
      // 返回cell
      return cell;
      
      • 调用pBlock变量保存的代码
      
      -(IBAction)plusClick {
      // 设置减号按钮可以点击
      self.minusButton.enabled = YES;
      // 设置模型数据加1
      self.wine.count++;
      // 刷新
      self.countLabel.text = [NSString stringWithFormat:@"%zd", self.wine.count];
      
      if (_pBlock) { // 判断
          _pBlock(WineCellTypePlus); // 调用block
      }
      }
      
      • OK,大功告成

    相关文章

      网友评论

        本文标题:block使用浅析

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