block使用淺析

  • 實(shí)現(xiàn)目標(biāo),給鍵盤添加一個(gè)工具條

LZKeyboardTool.xib圖:

// 自定義工具條
// LZKeyboardTool.h
#import <UIKit/UIKit.h>

typedef enum {
    KeyboardItemTypePrevious, // 上一個(gè)
    KeyboardItemTypeNext, // 下一個(gè)
    KeyboardItemTypeDone // 完成
} KeyboardItemType;

// 定義一個(gè)類型
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
{
    // 創(chuàng)建一個(gè)對(duì)象
    _instance = [[[NSBundle mainBundle] loadNibNamed:@"LZKeyboardTool" owner:nil options:nil] lastObject];

}
// 單例對(duì)象
+ (instancetype)sharekeyboardTool
{
    return _instance;
}

// 上一個(gè)
- (IBAction)previous:(id)sender {
    if (_pBlock) { // 先判斷
        _pBlock(KeyboardItemTypePrevious); // 調(diào)用block
    }
}

// 下一個(gè)
- (IBAction)next:(id)sender {
    if (_pBlock) { // 先判斷
        _pBlock(KeyboardItemTypeNext); // 調(diào)用block
    }
}
// 完成
- (IBAction)done:(id)sender {
    if (_pBlock) { // 先判斷
        _pBlock(KeyboardItemTypeDone); // 調(diào)用block
    }
}

@end


// ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end


// ViewController.m
#import "ViewController.h"
#import "LZKeyboardTool.h"

@interface ViewController ()
{
    NSArray *_fields; // 存儲(chǔ)所有的textField
}

// 生日框
@property (weak, nonatomic) IBOutlet UITextField *birthdayField;
// 輸入框容器
@property (weak, nonatomic) IBOutlet UIView *inputContainer;
/** LZKeyboard數(shù)據(jù)*/
@property (nonatomic, strong) LZKeyboardTool *tool;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 1.初始化自定義鍵盤
    [self setupCustomKeyboard];

    // 創(chuàng)建自定義鍵盤
    self.tool = [LZKeyboardTool sharekeyboardTool];

    // 2.設(shè)置每一個(gè)textfield的鍵盤工具view(inputAccessoryView)
    [self setupKeyboardTool];

    // 3.監(jiān)聽鍵盤的事件
    [self setupKeyboardNotification];

    // 含義,弱引用,防止循環(huán)引用
    // #define WeakSelf __weak typeof(self) weakSelf = self;
    __weak typeof(self) weakSelf = self;

    // 用block保存一段代碼
    self.tool.pBlock = ^ (KeyboardItemType itemType){
        // 獲取當(dāng)前響應(yīng)者的索引
        int currentIndex = [weakSelf getCurrentResponderIndex];

        switch (itemType) {
            case KeyboardItemTypePrevious:
                NSLog(@"上一個(gè)");
                [weakSelf showPreviousField:currentIndex];
                break;
            case KeyboardItemTypeNext:
                [weakSelf showNextField:currentIndex];
                break;
            case KeyboardItemTypeDone:
                [weakSelf touchesBegan:nil withEvent:nil];
                break;
        }

    };

}

// 獲取當(dāng)前textField的響應(yīng)者索引
// 如果返回-1代理沒有找到響應(yīng)者
- (int)getCurrentResponderIndex
{
    // 遍歷所有的textField獲取響應(yīng)者
    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.設(shè)置每一個(gè)textfield的鍵盤工具view(inputAccessoryView)
- (void)setupKeyboardTool
{
    // 創(chuàng)建工具欄
    LZKeyboardTool *tool = [LZKeyboardTool sharekeyboardTool];

    // 1.獲取輸入框窗口的所有子控件
    NSArray *views = self.inputContainer.subviews;

    // 創(chuàng)建一個(gè)數(shù)據(jù)存儲(chǔ)textfield
    NSMutableArray *fieldsM = [NSMutableArray array];

    // 2.遍歷
    for (UIView *child in views) {
        // 如果子控制器是UITextField的時(shí)候,設(shè)置inputAccessoryView
        if ([child isKindOfClass:[UITextField class]]) {
            UITextField *tf = (UITextField *)child; // 類型轉(zhuǎn)換
            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值
    // 鍵盤結(jié)束時(shí)的fm
    CGRect kbEndFrm = [notifi.userInfo[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];

    // 鍵盤結(jié)束時(shí)的y
    CGFloat kEndY = kbEndFrm.origin.y;

    // 獲取當(dāng)前的響應(yīng)者
    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; // 恢復(fù)到原來位置
        }];
    }

}

#pragma mark -鍵盤工具條的代理

// 讓上一個(gè)field成為響應(yīng)者
- (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; // 恢復(fù)到原來位置
    }];
}

@end

效果圖片:

  • 筆者主要是想通過該示例程序來說明一下筆者使用block的步驟 - 在LZKeyboardTool.h里面定義一個(gè)block類型,并且定義了一個(gè)pBlock變量

    // 定義一個(gè)類型
    typedef void (^myBlock)(KeyboardItemType);
    
    @interface LZKeyboardTool : UIView
    
    + (instancetype)sharekeyboardTool;
    
    @property (nonatomic, copy) myBlock pBlock;
    
    @end
    
    • 在ViewController.m里面使用pBlock變量保存了一段代碼,里面比如把用到的self(指代的是控制器)重新定義了一個(gè)弱指針指向了它,為了防止在pBlock保存的代碼中使用到self而造成循環(huán)引用,這里筆者也是網(wǎng)上百度搜索到的,這里也恰好是重點(diǎn)
    // 含義,弱引用,防止循環(huán)引用
    __weak typeof(self) weakSelf = self;
    
    // 用block保存一段代碼
    self.tool.pBlock = ^ (KeyboardItemType itemType){
        // 獲取當(dāng)前響應(yīng)者的索引
        int currentIndex = [weakSelf getCurrentResponderIndex];
    
        switch (itemType) {
            case KeyboardItemTypePrevious:
                NSLog(@"上一個(gè)");
                [weakSelf showPreviousField:currentIndex];
                break;
            case KeyboardItemTypeNext:
                [weakSelf showNextField:currentIndex];
                break;
            case KeyboardItemTypeDone:
                [weakSelf touchesBegan:nil withEvent:nil];
                break;
        }
    
    };
    
    • 在LZKeyboardTool.m里面調(diào)用pBlock保存的代碼
    // 上一個(gè)
    -(IBAction)previous:(id)sender {
    if (_pBlock) { // 先判斷
        _pBlock(KeyboardItemTypePrevious); // 調(diào)用block
    }
    }
    
    • OK,結(jié)束,就這么簡單
  • 下面在來一個(gè)示例代碼,不是全部的,主要是為了說明block怎么去使用,首先通過下面這張圖片來表達(dá)筆者的意圖,點(diǎn)擊加號(hào)按鈕或者減號(hào)按鈕,得控制下面顯示的總價(jià)格,第一,加號(hào)按鈕和減號(hào)按鈕位于tableView上,下面那一個(gè)UIView是與tableView分隔開的,也就是他倆不能進(jìn)行數(shù)據(jù)的交互,要實(shí)現(xiàn)兩者之間交互,可以通過 通知,代理, KVO, block,這里筆者就使用了block來實(shí)現(xiàn),主要是為了說明block的用法


// tableView的cell模型

// XMGWineCell.h
#import <UIKit/UIKit.h>
@class XMGWine;

typedef enum {
    WineCellTypePlus,
    WineCellTypeMinus
} WineCellType;

// 定義一個(gè)類型
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 {
    // 設(shè)置模型數(shù)據(jù)-1
    self.wine.count--;
    // 判斷r
    if (self.wine.count == 0) { // 減號(hào)不能點(diǎn)擊
        self.minusButton.enabled = NO;
    }
    // 賦值
    self.countLabel.text = [NSString stringWithFormat:@"%zd", self.wine.count];

    if (_pBlock) { // 判斷
        _pBlock(WineCellTypeMinus); // 調(diào)用block
    }
}
/**
 *  +
 */
- (IBAction)plusClick {
    // 設(shè)置減號(hào)按鈕可以點(diǎn)擊
    self.minusButton.enabled = YES;
    // 設(shè)置模型數(shù)據(jù)加1
    self.wine.count++;
    // 刷新
    self.countLabel.text = [NSString stringWithFormat:@"%zd", self.wine.count];

    if (_pBlock) { // 判斷
        _pBlock(WineCellTypePlus); // 調(diào)用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;
/** 酒模型數(shù)組*/
@property (nonatomic, strong) NSArray *wineArray;
/** 總價(jià)格*/
@property (weak, nonatomic) IBOutlet UILabel *totalPriceLabel;

/** 購物車對(duì)象(存放需要購買的商品) */
@property (nonatomic, strong) NSMutableArray *wineCar;
@end

@implementation ViewController

#pragma mark - 懶加載數(shù)據(jù)

- (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
{
    // 計(jì)算總價(jià)
    int totalPrice = self.totalPriceLabel.text.intValue + wineCell.wine.money.intValue;

    // 設(shè)置總價(jià)
    self.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];

    self.buyCount.enabled = YES;

    // 如果這個(gè)商品已經(jīng)在購物車中,就不用再添加
    if ([self.wineCar containsObject:wineCell.wine]) return;

    // 添加需要購買的商品
    [self.wineCar addObject:wineCell.wine];
}

- (void)wineCellDidClickMinusButton:(XMGWineCell *)wineCell
{
    // 計(jì)算總價(jià)
    int totalPrice = self.totalPriceLabel.text.intValue - wineCell.wine.money.intValue;

    // 設(shè)置總價(jià)
    self.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];

    self.buyCount.enabled = totalPrice > 0;

    // 將商品從購物車中移除
    if (wineCell.wine.count == 0) {
        [self.wineCar removeObject:wineCell.wine];
    }
}

/**
 *  購買
 */
- (IBAction)buy{
    // 循環(huán)
    for (XMGWine *wine in self.wineArray) {
        if (wine.count) { // 判斷
            NSLog(@"[%@]賣了%zd瓶", wine.name, wine.count);
        }
    }
}

/**
 *  清空
 */
- (IBAction)clear {
    // 模型數(shù)據(jù)
    for (XMGWine *wine in self.wineArray) {
        wine.count = 0;
    }
    // 刷新數(shù)據(jù)
    [self.tableView reloadData];
    // 設(shè)置購買按鈕不可點(diǎn)擊
    self.buyCount.enabled = NO;
    // 設(shè)置總價(jià)格totalPriceLabel值為0
    self.totalPriceLabel.text = @"0";
}


#pragma mark - UITableViewDataSource方法
/**
 * 每組多少行
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.wineArray.count;
}

/**
 *  每行顯示什么內(nèi)容
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 去緩存里面找
    // 含義,弱引用,防止循環(huán)引用
    __weak typeof(self) weakSelf = self;

    static NSString *ID = @"wine";
    // 如果在block中訪問了外界對(duì)象,那么就得加上__block修飾符
    __block __weak XMGWineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    // 賦值
    XMGWine *wine = self.wineArray[indexPath.row];
    cell.wine = wine;
    // 設(shè)置
    cell.pBlock = ^ (WineCellType itemType){
        int totalPrice = 0;
        switch (itemType) {
            case WineCellTypePlus:
                // 計(jì)算總價(jià)
                totalPrice = weakSelf.totalPriceLabel.text.intValue + cell.wine.money.intValue;

                // 設(shè)置總價(jià)
                weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];

                weakSelf.buyCount.enabled = YES;
                // 如果這個(gè)商品已經(jīng)在購物車中,就不用再添加
                if ([weakSelf.wineCar containsObject:cell.wine]) return;
                // 添加需要購買的商品
                [weakSelf.wineCar addObject:cell.wine];

                break;

            case WineCellTypeMinus:
                // 計(jì)算總價(jià)
                totalPrice = weakSelf.totalPriceLabel.text.intValue - cell.wine.money.intValue;

                // 設(shè)置總價(jià)
                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里面定義一個(gè)block類型,并且定義一個(gè)pBlock變量
    // 定義一個(gè)類型
    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];很關(guān)鍵,因?yàn)閜Block變量保存的代碼中用到了外界對(duì)象,外界對(duì)象得加上__block修飾符才能防止循環(huán)引用,就這么簡單
    // 去緩存里面找
    // 含義,弱引用,防止循環(huán)引用
    __weak typeof(self) weakSelf = self;
    
    static NSString *ID = @"wine";
    // 如果在block中訪問了外界對(duì)象,那么就得加上__block修飾符
    __block __weak XMGWineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    // 賦值
    XMGWine *wine = self.wineArray[indexPath.row];
    cell.wine = wine;
    // 設(shè)置
    cell.pBlock = ^ (WineCellType itemType){
        int totalPrice = 0;
        switch (itemType) {
            case WineCellTypePlus:
                // 計(jì)算總價(jià)
                totalPrice = weakSelf.totalPriceLabel.text.intValue + cell.wine.money.intValue;
    
                // 設(shè)置總價(jià)
                weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%zd", totalPrice];
    
                weakSelf.buyCount.enabled = YES;
                // 如果這個(gè)商品已經(jīng)在購物車中,就不用再添加
                if ([weakSelf.wineCar containsObject:cell.wine]) return;
                // 添加需要購買的商品
                [weakSelf.wineCar addObject:cell.wine];
    
                break;
    
            case WineCellTypeMinus:
                // 計(jì)算總價(jià)
                totalPrice = weakSelf.totalPriceLabel.text.intValue - cell.wine.money.intValue;
    
                // 設(shè)置總價(jià)
                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;
    
    • 調(diào)用pBlock變量保存的代碼
    
    -(IBAction)plusClick {
    // 設(shè)置減號(hào)按鈕可以點(diǎn)擊
    self.minusButton.enabled = YES;
    // 設(shè)置模型數(shù)據(jù)加1
    self.wine.count++;
    // 刷新
    self.countLabel.text = [NSString stringWithFormat:@"%zd", self.wine.count];
    
    if (_pBlock) { // 判斷
        _pBlock(WineCellTypePlus); // 調(diào)用block
    }
    }
    
    • OK,大功告成
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Block使用場(chǎng)景,可以在兩個(gè)界面的傳值,也可以對(duì)代碼封裝作為參數(shù)的傳遞等。用過GCD就知道Block的精妙之處。...
    Coder_JMicheal閱讀 814評(píng)論 2 1
  • 目錄 Block概述 Block定義方式 Block保存代碼 Block傳值 Block對(duì)外部變量的傳遞 Bloc...
    子斌閱讀 1,493評(píng)論 2 7
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對(duì)C語言的擴(kuò)展,用來實(shí)現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,466評(píng)論 2 26
  • 在介紹Block之前通過一個(gè)簡單的應(yīng)用場(chǎng)景認(rèn)識(shí)下Block 場(chǎng)景描述如下:TableView上面有多個(gè)Custom...
    黑_白_灰閱讀 1,471評(píng)論 4 29
  • block.png iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對(duì)C語言的擴(kuò)展,用來實(shí)...
    全棧農(nóng)民工閱讀 638評(píng)論 0 1

友情鏈接更多精彩內(nèi)容