iOS_21团购_通过block对请求工具类的代理方法进行二次封装

最终效果图:

【点评】提供的工具类DPAPI 在请求完毕后,使用的是代理机制,当一次请求成功或者失败时,会调用代理的相应方法

为了将点评提供的工具类DPAPI进行二次封装,

再次定义了一个block:

typedef  void(^RequestDoneCallBackBlock)(id deals,NSError *err);

该block有两个参数,

分别是成功时,服务器返回的deals字典数组

另一个参数是:请求失败时,服务器返回的失败信息

两个参数分别对应代理的两个方法(即成功、失败时分别调用的代理方法)

该block调用时机是:

在一次DPAPI请求完成后,无论失败和成功都要在代理方法中调用的该block,

将该次请求的请求结果进行回传给工具类,

工具类内部再通过对回传结果进行判断,

进而决定要不要调用外部的successBlock或者failBlock

本block是与一个请求对应,并且存入成员字典中保存,

目的是处理并发请求时,确保一次请求与一个请求结果的回调block一一对应

工具类:

DealRequestTool.h

//
//  DealRequestTool.h
//  帅哥_团购
//
//  Created by beyond on 14-8-19.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//  单例,使用二次block封装向服务器发送请求的所有代码

#import <Foundation/Foundation.h>
// 定义请求成功后调用的block,将服务器返回的字典数组转成对象数组后,回传
typedef  void(^successBlock)(NSArray *deals);
// 定义请求失败后调用的block,将服务器返回的出错信息回传
typedef void(^failBlock)(NSError *error);

@interface DealRequestTool : NSObject
singleton_interface(DealRequestTool)

// 对象方法,内部封装了向服务器提交的参数字典(从工具类获取),并且通过调用自定义方法,使用二次block封装了DPAPI的代理方法
- (void)dealRequestWithPageNo:(int)pageNo success:(successBlock)successBock     fail:(failBlock)failBlock;
@end

工具类:

DealRequestTool.m

//
//  DealRequestTool.m
//  帅哥_团购
//
//  Created by beyond on 14-8-19.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//  单例,使用二次block封装向服务器发送请求的所有代码

#import "DealRequestTool.h"
#import "MetaDataTool.h"
#import "City.h"
#import "DPAPI.h"
#import "Deal.h"
#import "Order.h"

// 重要,定义一个  一次DPAPI请求完成后,无论失败和成功都会在代理方法中调用的block,本block与一个请求对应,存入成员字典中保存,目的是处理并发时,确保一次请求与一个请求结果的回调block一一对应
typedef  void(^RequestDoneCallBackBlock)(id deals,NSError *err);
@interface DealRequestTool ()<DPRequestDelegate>
{
    // 重要,每一次请求,对应一个RequestDoneCallBackBlock,并且在DPAPI的代理方法里面,在请求结束后,调用RequestDoneCallBackBlock回传服务器的成功或失败信息,因只需初始化一次,在Init方法
    NSMutableDictionary *_requestBlockDict;
}

@end
@implementation DealRequestTool
singleton_implementation(DealRequestTool)

- (id)init
{
    if (self = [super init]) {
        // 重要,每一次请求,对应一个requestBlock,并且在代理方法里面调用requestBlock回传服务器的成功或失败信息,只需初始化一次,在Init方法
        _requestBlockDict = [NSMutableDictionary dictionary];
    }
    return self;
}

// 1.对象方法,供外部调用.内部封装了向服务器提交的参数字典(从工具类获取),并且通过调用自定义方法,使用二次block封装了DPAPI的代理方法
- (void)dealRequestWithPageNo:(int)pageNo success:(successBlock)successBock     fail:(failBlock)failBlock
{
    NSMutableDictionary *paramsDict = [NSMutableDictionary dictionary];
    // 1.从工具类中获取所有的请求参数,即当前城市、商区、排序等
    [paramsDict addEntriesFromDictionary:[self getAllRequestParamsDict]];
    // 1.1.添加页码参数(int转成string)
    [paramsDict setObject:@(pageNo) forKey:@"page"];

    // 2.重要~~~~调用自定义方法,发送DPAPI请求
    [self requestWithUrl:@"v1/deal/find_deals" params:paramsDict requestBlock:^(id deals, NSError *err) {
        // 这儿,就可以拿到与本次请求相对应的回调block,参数里已经包含了本次请求的成功字典数组 和 失败信息
        // 现在只需判断,请求的回调block  有没有成功的返回结果,如果有,并且外界调用者需要结果 ,才把相应的请求结果 再次回调给外界
        if (deals) {
            if (successBock) {

                NSMutableArray *dealsArr = [NSMutableArray array];
                // 从返回结果根据Key,取出所有的字典数组,一一遍历,转成模型
                NSArray *arr = deals[@"deals"];
                for (NSDictionary *dict in arr) {
                    Deal *deal = [[Deal alloc]init];
                    [deal setValuesWithDict:dict];
                    [dealsArr addObject:deal];
                }
                // 将封装好的模型数组回调给外界调用者(格子显示数据)
                successBock(dealsArr);
            }
        } else {
            // 同样,请求的回调block  有没有失败的信息,如果有,并且外界调用者需要结果 ,才把相应的请求结果 再次回调给外界
            if (failBlock) {
                failBlock(err);
            }
        }

    }];

}
// 自定义方法,从工具类撮所有的请求参数
- (NSDictionary *)getAllRequestParamsDict
{
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    // 1.1.添加城市参数
    NSString *city = [MetaDataTool sharedMetaDataTool].currentCity.name;
    [params setObject:city forKey:@"city"];

    // 1.2.添加区域参数
    NSString *district = [MetaDataTool sharedMetaDataTool].currentDistrictName;
    if (district && ![district isEqualToString:kAllDistrict]) {
        [params setObject:district forKey:@"region"];
    }

    // 1.3.添加分类参数
    NSString *category = [MetaDataTool sharedMetaDataTool].currentCategoryName;
    if (category && ![category isEqualToString:kAllCategory]) {
        [params setObject:category forKey:@"category"];
    }
    // 1.4.添加排序参数
    Order *order = [MetaDataTool sharedMetaDataTool].currentOrder;
    if (order) {
        // 按照其他方式排序
        [params setObject:@(order.index) forKey:@"sort"];
    }
    return params;
}
// 2.调用自定义方法,发送DPAPI请求,并且DPAPI请求结束后,会在代理方法中调用本次请求对应的requestDoneCallBackBlock
- (void)requestWithUrl:(NSString *)url params:params requestBlock:(RequestDoneCallBackBlock)callBackBlock
{
    DPAPI *api = [DPAPI sharedDPAPI];
    // 重要~~~必须返回本次请求的DPRequest,并且与requestBlock一一对应,存入字典里面,因为可能出现并发请求的情况,如果不将回调requestBlock与DPRequest一一对应起来,就会出现下一次请求的结果 覆盖上一次请求的情况发生
    DPRequest *dpRequest= [api requestWithURL:url params:params delegate:self];
    // 重要~~~用成员变量_requestBlockDict记住本次的请求,和与之对应的回调requestBlock,在代理方法就可以从字典中取出,设置回调block的参数(_deals或者错误信息)
    [_requestBlockDict setObject:callBackBlock forKey:dpRequest.description];
}
#pragma mark - 代理方法
// 一次请求成功时调用,参数:该次请求的请求对象,该次请求的请求结果
- (void)request:(DPRequest *)request didFinishLoadingWithResult:(id)result
{
    // 先从成员字典中,根据本次的请求对象,取出本次请求的回调block
    RequestDoneCallBackBlock callBackBlock = [_requestBlockDict objectForKey:request.description];
    // 直接回调与本次请求对应的block,并将请求结果(deals字典数组)回传,因本代理方法是请求成功时调用,故回调block的失败参数不填写
    callBackBlock(result,nil);
}
// 一次请求失败时调用,参数:该次请求的请求对象,该次请求的失败原因
- (void)request:(DPRequest *)request didFailWithError:(NSError *)error
{
    // 先从成员字典中,根据本次的请求对象,取出本次请求的回调block
     RequestDoneCallBackBlock callBackBlock = [_requestBlockDict objectForKey:request.description];
    // 直接回调与本次请求对应的block,并将失败原因回传,因本代理方法是请求失败时调用,故回调block的成功参数不填写
    callBackBlock(nil,error);
}
@end

外部调用者:即控制器

DealListController.m

//
//  DealListController.m
//  帅哥_团购
//
//  Created by beyond on 14-8-14.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//  点击dock上面的【团购】按钮对应的控制器,上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大按钮(TopMenu)(内部由三个小按钮组成<TopMenuItem>)

#import "DealListController.h"
// 导航栏左边是一个大按钮(顶部菜单)
#import "TopMenu.h"
// 封装的自定义cell
#import "DealCell.h"
// 点评提供的封装发送请求的类
#import "DPAPI.h"
// 工具类
#import "MetaDataTool.h"
// 封装请求的工具类
#import "DealRequestTool.h"
// 模型类
#import "City.h"
#import "Deal.h"

#define kItemW 250
#define kItemH 250
@interface DealListController()<DPRequestDelegate>
{
    // 用于接收服务器返回的字典数组----转化成的对象数组,供格子们显示
    NSMutableArray *_deals;
}
@end
@implementation DealListController
// 覆盖控制器的init方法
- (id)init
{
    // 创建一个流布局
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    // 设置流布局里面的每一个格子宽和高,即每一个网格的尺寸
    layout.itemSize = CGSizeMake(kItemW, kItemH);

    // 调用父类UICollectionViewController的initWithCollectionViewLayout方法,(self这儿找不到,会到父类里去找方法)
    return [self initWithCollectionViewLayout:layout];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    _deals = [NSMutableArray array];
    // 0.监听所有改变的通知(如城市、商区、分类、排序)
    kAddAllNotes(dataChanged)

    // 1.顶部导航栏的基本设置
    [self setNavigationBar];

    // 2.collectionView的基本设置
    [self setCollectionView];
}
// 0.监听到城市等更改时,向服务器发出请求
- (void)dataChanged
{
    // 重要~~~~~调用封装好请求工具类,发送请求,参数:页码数,
    [[DealRequestTool sharedDealRequestTool]dealRequestWithPageNo:1 success:^(NSArray *deals) {
            // 先移除旧的数据
            [_deals removeAllObjects];
            // 再将封装好的对象数组加到成员变量
            [_deals addObjectsFromArray:deals];
            // 接下来就可以给collectionView提供数据源了
            [self.collectionView reloadData];
    } fail:^(NSError *error) {
            log(@"request---fail:%@",error);
    }];
}

// 2.顶部导航栏的基本设置
- (void)setNavigationBar
{

    // 1.右边的搜索框
    UISearchBar *s = [[UISearchBar alloc] init];
    s.frame = CGRectMake(0, 0, 210, 35);
    s.placeholder = @"请输入商品名、地址等";
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:s];

    // 2.左边的菜单栏
    TopMenu *top = [[TopMenu alloc] init];
    // 重要,TopMenu里面的item点击后,创建的PopMenu将要添加到哪儿去???就是本控制器的view
    top.controllerView = self.view;
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:top];

}
// 3.collectionView的基本设置
- (void)setCollectionView
{
    // 1.设置collectionView的背景色,(不像tableViewController,本控制器的view是UIView,在UIView里面再添加的collectionView)
    self.collectionView.backgroundColor = kGlobalBg;

    // 2.注册cell要用到的xib
    [self.collectionView registerNib:[UINib nibWithNibName:@"MyDealCell" bundle:nil] forCellWithReuseIdentifier:@"DealCell"];

    // 3.设置collectionView永远支持垂直滚动,为下拉刷新准备(弹簧)
    self.collectionView.alwaysBounceVertical = YES;

}

// 4.重要~~~因为在控制器创建时,宽默认是768,高默认是1024,不管横竖屏
// 只有在viewWillAppear和viewDidAppear方法中,可以取得view最准确的(即实际的)宽和高(width和height)
- (void)viewWillAppear:(BOOL)animated
{
    // 默认计算layout
    [self didRotateFromInterfaceOrientation:0];
}

#pragma mark - 父类方法

// 拦截,屏幕即将旋转的时候调用(控制器监控屏幕旋转)
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    log(@"屏幕即将旋转");
}

// 拦截,屏幕旋转完毕的时候调用
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    // 1.取出创建CollectionViewController时传入的的UICollectionViewFlowLayout
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;

    // 2.计算间距
    CGFloat v = 0;
    CGFloat h = 0;
    CGFloat height = self.view.frame.size.height -44;
    CGFloat width = self.view.frame.size.width;
    if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)
        ) {
        // 横屏的间距
        v = (height - 2 * kItemH) / 3;
        h = (width - 3 * kItemW) / 4;

    } else {
        // 竖屏的间距
        v = (height - 3 * kItemH) / 4;
        h = (width - 2 * kItemW) / 3;
    }
    // 3.动画调整格子之间的距离
    [UIView animateWithDuration:4.0 animations:^{
        // 上 左 下 右 四个方向的margin
        layout.sectionInset = UIEdgeInsetsMake(h, h, v, h);
        // 每一行之间的间距
        layout.minimumLineSpacing = h;
    }];
}

#pragma mark - collectionView代理方法
// 共有多少个Item(就是格子Cube)
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _deals.count;
}
// 生成每一个独一无二的格子
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"DealCell";
    DealCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

    // 设置独一无二的数据
    cell.deal = _deals[indexPath.row];
    // 返回cell
    return cell;
}  <span style="white-space:pre">			</span>@end

iOS_21团购_通过block对请求工具类的代理方法进行二次封装

时间: 2024-10-14 19:13:52

iOS_21团购_通过block对请求工具类的代理方法进行二次封装的相关文章

iOS_21团购_发送请求获取【点评】数据

请求结果简单显示: 用到的点评封装的类: 使用tableView简单展示: // // DealListController.m // 帅哥_团购 // // Created by beyond on 14-8-14. // Copyright (c) 2014年 com.beyond. All rights reserved. // 点击dock上面的[团购]按钮对应的控制器,上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大按钮(TopMenu)(内部由三个小按钮组成<TopM

iOS_21团购_【运行时】将字典转成对象模型

最终效果图: 核心代码: NSObject+Dict.h // // NSObject+Dict.h // 帅哥_团购 // // Created by beyond on 14-8-14. // Copyright (c) 2014年 com.beyond. All rights reserved. // 使用运行时,将dict转成对象 #import <Foundation/Foundation.h> @interface NSObject (Dict) // 一个对象,调用此方法,参数

iOS_21团购_拼音搜索

最终效果图: 关键代码: 搜索结果控制器: // // SearchResultController.m // 帅哥_团购 // // Created by beyond on 14-8-15. // Copyright (c) 2014年 com.beyond. All rights reserved. // 当搜索框searchBar里面的文字change的时候,会创建本控制器,展示搜索结果列表,本控制器只有唯一一个成员变量,那就是从CityLocationController控制器的sea

iOS_21团购_发送请求【点评】数据

结果表明,一个简单的请求: 用到的点评封装的类: 使用tableView简单展示: // // DealListController.m // 帅哥_团购 // // Created by beyond on 14-8-14. // Copyright (c) 2014年 com.beyond. All rights reserved. // 点击dock上面的[团购]button相应的控制器,上面是导航栏,导航栏右边是searchBar,导航栏左边是一个大button(TopMenu)(内部由

iOS_21团购_顶部菜单和弹出菜单联动

最后效果图: 各控件关系图1: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >\ 各控件关系图2: 点击Dock上面的buttonDockItem, 创建经导航控制器包装的DealListController, 而且加入到主控制器的右側空间 // // Deal

iOS_21团购_定位并根据经纬度,解析出城市名

// // LocationTool.m // 帅哥_团购 // // Created by beyond on 14-8-22. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "LocationTool.h" #import <CoreLocation/CoreLocation.h> #import "City.h" #import "MetaDataTo

iOS_21团购_地图功能

最终效果图: 右下角的回到用户位置按钮: MapController控制器, 是主控制器左侧dock上面的[地图]按钮对应的控制器, 继承自ShowDealDetailController, 因此,自动拥有了展示团购详情控制器的功能 // // MapController.h // 帅哥_团购 // // Created by beyond on 14-8-14. // Copyright (c) 2014年 com.beyond. All rights reserved. // dock上面的

iOS_21团购_真正封装的团购详情控制器

最终效果图: DealDetailController控制器 // // DealDetailController.h // 帅哥_团购 // // Created by beyond on 14-8-20. // Copyright (c) 2014年 com.beyond. All rights reserved. // 真正的通过xib显示一个订单的详情的控制器 #import <UIKit/UIKit.h> @class Deal; @interface DealDetailContr

iOS_21团购_iPad中子控制器的屏幕适配

最终效果图: iPad中重要的屏幕适配技巧, 先添加一个contentView占位, 并且只在viewDidLoad方法中设置frame, 因为iPad中无论横屏还是竖屏,W都是窄的那一条边, 因此,先添加一个contentView到主控制器的右侧, 并让它随着主控制器宽高自动伸缩, 而子控制器的view只需添加到contentView中即可,x 0 y 0宽高就是contentView的bounds 代码片段: // // MainViewController.m // 帅哥_团购 // //