IOS-小项目(饿了么 网络部分 简单实现)

Posted 旭宝爱吃鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOS-小项目(饿了么 网络部分 简单实现)相关的知识,希望对你有一定的参考价值。

在介绍小项目之前,在此说明一下此代码并非本人所写,我只是随笔的整理者。

在介绍之前先展现一下效果图。

看过效果图大家应该很熟悉了,就是饿了么的一个界面而已,值得注意的是,实现时并没有采用本地连接,而是实打实的网络连接。看一下文件架构。

这一采用的是MVC设计模式,虽然文件很少,但是也可以看。

下面开始正式介绍小项目的实现。

首先介绍Model的实现,很简单,实现模型即可,

Shop.h

//
//  Shop.h
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Shop : NSObject
//建立Shop模型
@property (nonatomic, copy) NSString * address;

@property (nonatomic, copy) NSString * name;

@property (nonatomic, copy) NSString * image_path;

//-description这个方法系统占用
@property (nonatomic, copy) NSString * desc;

@end

 Shop.m

//
//  Shop.m
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import "Shop.h"

@implementation Shop

@end

model实现后我们并没有真正的实现方法,下面也不是,接下来是对AFN的封装,之所以封装是因为,我们难以保证在以后该三方还能存在,只要封装,哪怕以后没有了AFN我们也可以在封装框架里运用其他的三方实现。

HttpClient.h HttpClient.m

//
//  HttpClient.h
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//


#import <Foundation/Foundation.h>
#import "AFNetworking.h"

//HTTP请求类别
typedef NS_ENUM(NSInteger,HttpRequestType) {
    HttpRequestGet,
    HttpRequestPost,
    HttpRequestPut,
    HttpRequestDelete,
};


/**
 *  请求前预处理block
 */
typedef void(^PrepareExecuteBlock)(void);

typedef void(^SuccessBlock)(NSURLSessionDataTask * task, id responseObject);

typedef void(^FailureBlock)(NSURLSessionDataTask * task, NSError * error);

@interface HttpClient : NSObject

+ (HttpClient *)defaultClient;

/**
 *  HTTP请求(GET,POST,PUT,DELETE)
 *
 *  @param url     请求地址
 *  @param method  请求类型
 *  @param params  请求参数
 *  @param prepare 请求前预处理
 *  @param success 请求成功处理
 *  @param failure 请求失败处理
 */

- (void)requestWithPath:(NSString *)url
                 method:(NSInteger)method
            paramenters:(NSDictionary *)params
         prepareExecute:(PrepareExecuteBlock)prepare
                success:(SuccessBlock)success
                failure:(FailureBlock)failure;

@end
//
//  HttpClient.m
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import "HttpClient.h"

@interface HttpClient ()

@property (nonatomic, strong) AFHTTPSessionManager * manager;

@property (nonatomic, assign) BOOL isConnect;

@end

@implementation HttpClient

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        self.manager = [AFHTTPSessionManager manager];
        //设置请求类型
        self.manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        //设置响应类型
        self.manager.responseSerializer = [AFJSONResponseSerializer serializer];
        
        self.manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/html", @"text/json", @"text/javascript",@"text/plain",@"image/gif", nil];
        
        //开启监听
        [self openNetMonitoring];
        
    }
    return self;
}
//判断是否有网络连接,有网络连接再进行下一操作。
- (void)openNetMonitoring {
    
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                self.isConnect = NO;
                break;
            case AFNetworkReachabilityStatusNotReachable:
                self.isConnect = NO;
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                self.isConnect = YES;
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                self.isConnect = YES;
                break;
            default:
                break;
        }
        
    }];
    
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    
    self.isConnect = YES;
}
//单例
+ (HttpClient *)defaultClient {
    
    static HttpClient * instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (void)requestWithPath:(NSString *)url
                 method:(NSInteger)method
            paramenters:(NSDictionary *)params
         prepareExecute:(PrepareExecuteBlock)prepare
                success:(SuccessBlock)success
                failure:(FailureBlock)failure {
    
    
    if ([self isConnectionAvailable]) {
        
        //预处理
        if (prepare) {
            prepare();
        }
        
        switch (method) {
            case HttpRequestGet:
                [self.manager GET:url parameters:params progress:nil success:success failure:failure];
                break;
            case HttpRequestPost:
                [self.manager POST:url parameters:params progress:nil success:success failure:failure];
                break;
            case HttpRequestPut:
                [self.manager PUT:url parameters:params success:success failure:failure];
                break;
            case HttpRequestDelete:
                [self.manager DELETE:url parameters:params success:success failure:failure];
                break;
            default:
                break;
        }
        
    } else {
        
        [self showExceptionDialog];
    }
}

- (BOOL)isConnectionAvailable {
    
    return self.isConnect;
}
//如果,没有网络,弹出Alert
- (void)showExceptionDialog {
    
    [[[UIAlertView alloc] initWithTitle:@"提示" message:@"网络连接异常,请检查网络连接" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles: nil] show];
}

@end

接下来是对SVProgressHUD进行封装

TollHeper.h TollHeper.m

//
//  ToolHelper.h
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface ToolHelper : NSObject
/******************* 指示器方法  ****************/

//对指示器进行封装,如可出现新的主流三方,可以很好的给予升级

//弹出操作错误信息提示框
+ (void)showErrorMessage:(NSString *)message;
//弹出操作成功信息提示框
+ (void)showSuccessMessage:(NSString *)message;
//弹出加载提示框
+ (void)showProgressMessage:(NSString *)message;
//弹出用户信息
+ (void)showInfoMessage:(NSString *)message;
//取消弹出框
+ (void)dismissHUD;
@end
//
//  ToolHelper.m
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import "ToolHelper.h"
#import "SVProgressHUD.h"
@implementation ToolHelper

//弹出操作错误信息提示框
+ (void)showErrorMessage:(NSString *)message {
    
    [SVProgressHUD showErrorWithStatus:message];
    
}
//弹出操作成功信息提示框
+ (void)showSuccessMessage:(NSString *)message {
    
    [SVProgressHUD showSuccessWithStatus:message];
}
//弹出加载提示框
+ (void)showProgressMessage:(NSString *)message {
    
    [SVProgressHUD showWithStatus:message];
    
}
//弹出用户信息
+ (void)showInfoMessage:(NSString *)message {
    
    [SVProgressHUD showInfoWithStatus:message];
    
}
//取消弹出框
+ (void)dismissHUD {
    
    [SVProgressHUD dismiss];
}

@end

做完以上工作之后下面需要解决的便是上面遗留下来的问题,不知道大家有没有发现在model里的注释

//-description这个方法系统占用

 我们该怎么解决这个问题呢,那么我要说的便是三方啦。

MJ的三方解决了这一问题

MJExtensionConfig.h MJExtensionConfig.m

//
//  MJExtensionConfig.h
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface MJExtensionConfig : NSObject

@end
//
//  MJExtensionConfig.m
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import "MJExtensionConfig.h"
#import "MJExtension.h"
#import "Shop.h"

@implementation MJExtensionConfig


//程序启动一定会调用
+ (void)load {
    
    /**
     *  解决网络的JSON字段和本地模型属性名不一致的情况
     *
     *  @return 左边是本地属性名,右侧是网络JSON名
     */
    
    [Shop mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
        
        return @{@"desc" : @"description"};
    }];
    
}

@end

(对于load在前面的博客我有详细的解释,不清楚的朋友可以看看)

处理过后,为了方便对整个程序都能用到的便俩个 头文件进行优化,我们采用PCH文件

ELeMe.pch

//
//  ELeMe.pch
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#ifndef ELeMe_pch
#define ELeMe_pch

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif

#import "ToolHelper.h"

/***************SERVER HOST***************/

#define SERVER_HOST @"http://restapi.ele.me/v3"

/***************SERVER API***************/

//获取餐馆列表
#define API_GetRestaurantsList @"/restaurants"

#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width

#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height

#define shopInfoPageLimit 20


#endif /* ELeMe_pch */

写完代码我们需要设置一下

设置如下

 

其实整个小项目真的很简单,导致并没有什么可以仔细去想的,写多了就好了。

下面是主要环节(注释说明的很详尽了,我就不多说废话了)

ViewController.h ViewController.m

//
//  ViewController.h
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end
//
//  ViewController.m
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import "ViewController.h"
#import "HttpClient.h"
#import "SVProgressHUD.h"
#import "Shop.h"
#import "MJExtension.h"
#import "ShopIconCell.h"
#import "MJRefresh.h"

static NSString * identifier = @"ShopInfoCell";

@interface ViewController ()<UITableViewDataSource,UITableViewDelegate>

//分页的页数
@property (nonatomic, assign) NSInteger page;

@property (nonatomic, strong) UITableView * tableView;

@property (nonatomic, strong) NSMutableArray * dataList;

@end

@implementation ViewController

- (NSMutableArray *)dataList {
    
    if (!_dataList) {
        _dataList = [NSMutableArray array];
    }
    return _dataList;
}

- (UITableView *)tableView {
    
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
        _tableView.dataSource =self;
        _tableView.delegate =self;
        [_tableView registerClass:[ShopInfoCell class] forCellReuseIdentifier:identifier];
        _tableView.rowHeight = 80;
    }
    return _tableView;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return self.dataList.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    ShopInfoCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    
    cell.shop = self.dataList[indexPath.row];
    
    return cell;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    [self.view addSubview:self.tableView];
    
    //使用MJRefresh给我们的talbleView添加下拉刷新上拉加载控件
    
    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];
    
    self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];
    
    //    [self loadNewData];
    
    //进入界面执行下拉刷新方法
    [self.tableView.mj_header beginRefreshing];
    
    
}

//上拉加载更多

- (void)loadMoreData {
    
    
    NSString * urlString = [NSString stringWithFormat:@"%@%@",SERVER_HOST,API_GetRestaurantsList];
    
    
    NSDictionary * params = @{@"full_image_path":@"1",
                              @"geohash":@"wx4u14w0649y",
                              @"limit":@(shopInfoPageLimit),
                              @"offset":@(self.page * shopInfoPageLimit),
                              @"type":@"geohash",
                              @"extras[]":@"food_activity",
                              @"extras[]":@"restaurant_activity"};
    
    
    [[HttpClient defaultClient] requestWithPath:urlString method:HttpRequestGet paramenters:params prepareExecute:^{
        
        [ToolHelper showProgressMessage:@"我在刷新"];
        
    } success:^(NSURLSessionDataTask *task, id responseObject) {
        NSLog(@"%@",responseObject);
        
        //JSON -> MODEL 有很多框架,原理都是KVC.
        
        //MJExtenstion  JsonModel  Mantle  YYModel
        
        //请求的分页数据 +1
        
        //当返回到数据的count值为0时,说明已经全部加载完毕。
        
        
        NSArray * shopList = [Shop mj_objectArrayWithKeyValuesArray:responseObject];
        
        //        if (shopList.count == 0) {
        //
        //            //显示已经全部加载完毕
        //            [self.tableView.mj_footer endRefreshingWithNoMoreData];
        //        }
        
        if (self.page > 2) {
            
            [self.tableView.mj_footer endRefreshingWithNoMoreData];
            
        } else  {
            
            [self.dataList addObjectsFromArray:shopList];
            
            [self.tableView reloadData];
            
            //将上拉加载控件弹下去
            [self.tableView.mj_footer endRefreshing];
            
            self.page ++ ;
        }
        
        
        
        [ToolHelper showSuccessMessage:@"请求成功"];
        
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        NSLog(@"%@",error);
        
        //将上拉加载控件弹下去
        [self.tableView.mj_footer endRefreshing];
        
        [ToolHelper showErrorMessage:@"请求失败"];
    }];
}


//下拉刷新

- (void)loadNewData {
    
    
    NSString * urlString = [NSString stringWithFormat:@"%@%@",SERVER_HOST,API_GetRestaurantsList];
    
    //下拉刷新,上拉加载  请求的地址是一样的 ,只是参数不一样的。
    //limit 是不变的、改变 offset。。
    //
    
    //下拉刷新是加载最新数据,将page页数至为0.
    //上拉加载加载更多数据。将page++
    
    //MJRefresh  EGO下拉刷新  UIRefreshCotrol 自已写上拉下拉
    
    NSDictionary * params = @{@"full_image_path":@"1",
                              @"geohash":@"wx4u14w0649y",
                              @"limit":@(shopInfoPageLimit),
                              @"offset":@(0),
                              @"type":@"geohash",
                              @"extras[]":@"food_activity",
                              @"extras[]":@"restaurant_activity"};
    
    
    [[HttpClient defaultClient] requestWithPath:urlString method:HttpRequestGet paramenters:params prepareExecute:^{
        
        [ToolHelper showProgressMessage:@"我在刷新"];
        
    } success:^(NSURLSessionDataTask *task, id responseObject) {
        NSLog(@"%@",responseObject);
        
        //JSON -> MODEL 有很多框架,原理都是KVC.
        
        //MJExtenstion  JsonModel  Mantle  YYModel
        
        //请求的分页数据 +1
        
        self.page = 1 ;
        
        //获取最新数据前删除之前的所有数据
        [self.dataList removeAllObjects];
        
        NSArray * shopList = [Shop mj_objectArrayWithKeyValuesArray:responseObject];
        
        [self.dataList addObjectsFromArray:shopList];
        
        [self.tableView reloadData];
        
        //重置没有更多的数据(消除没有更多数据的状态)
        [self.tableView.mj_footer resetNoMoreData];
        
        //将下拉刷新控件弹上去
        [self.tableView.mj_header endRefreshing];
        
        [ToolHelper showSuccessMessage:@"请求成功"];
        
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        NSLog(@"%@",error);
        
        //将下拉刷新控件弹上去
        [self.tableView.mj_header endRefreshing];
        [ToolHelper showErrorMessage:@"请求失败"];
    }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

大家是不是觉得少了些什么,当然是cell的自定义了,蛮简单了。看看就好。

ShopIconCell.h ShopIconCell.m

//
//  ShopIconCell.h
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import <UIKit/UIKit.h>

#import <UIKit/UIKit.h>
#import "Shop.h"

@interface ShopInfoCell : UITableViewCell

@property (nonatomic, strong) Shop * shop;

@end
//
//  ShopIconCell.m
//  CX-小项目(饿了么 网络部分 简单实现)
//
//  Created by ma c on 16/3/23.
//  Copyright © 2016年 xubaoaichiyu. All rights reserved.
//

#import "ShopIconCell.h"
#import "UIImageView+WebCache.h"

static CGFloat kMargin = 5;

@interface ShopInfoCell ()

@property (nonatomic, strong) UIImageView * iconView;

@property (nonatomic, strong) UILabel * nameLabel;

@property (nonatomic, strong) UILabel * descLabel;

@end

@implementation ShopInfoCell

-  (void)setShop:(Shop *)shop {
    
    _shop = shop;
        
    [self.iconView sd_setImageWithURL:[NSURL URLWithString:shop.image_path] placeholderImage:nil];
    
    self.nameLabel.text = shop.name;
    self.descLabel.text = shop.desc;
}

- (UIImageView *)iconView {
    
    if (!_iconView) {
        _iconView = [[UIImageView alloc] init];
    }
    return _iconView;
}

- (UILabel *)nameLabel {
    
    if (!_nameLabel) {
        _nameLabel = [[UILabel alloc] init];
    }
    return _nameLabel;
}

- (UILabel *)descLabel {
    
    if (!_descLabel) {
        _descLabel = [[UILabel alloc] init];
    }
    return _descLabel;
}

-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        
        [self.contentView addSubview:self.iconView];
        [self.contentView addSubview:self.nameLabel];
        [self.contentView addSubview:self.descLabel];
        
    }
    return self;
}

- (void)layoutSubviews {
    
    [super layoutSubviews];
    
    self.iconView.frame = CGRectMake(kMargin, kMargin, 70, 70);
    
    self.nameLabel.frame = CGRectMake(CGRectGetMaxX(self.iconView.frame) + kMargin, CGRectGetMinY(self.iconView.frame), SCREEN_WIDTH - CGRectGetMaxX(self.iconView.frame) - 2 * kMargin, 20);
    
    self.descLabel.frame = CGRectMake(CGRectGetMinX(self.nameLabel.frame), CGRectGetMaxY(self.nameLabel.frame) + kMargin, CGRectGetWidth(self.nameLabel.frame), 20);
    
    
}

- (void)awakeFromNib {
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    
}

@end

就这样一个小项目就这么的完成了。

以上是关于IOS-小项目(饿了么 网络部分 简单实现)的主要内容,如果未能解决你的问题,请参考以下文章

饿了么购物车动画

iOS 类似美团或饿了么评价中的星星评分控件

skecth怎么把饿了么组件变成自己的组件

按需引入饿了么UI组件

vuejs仿美团,饿了么项目之——商品数量加减篇

前端实战项目:Vue.js实现外卖平台webapp,饿了么项目的翻版