1. 程式人生 > >IOS-小項目(餓了麽 網絡部分 簡單實現)

IOS-小項目(餓了麽 網絡部分 簡單實現)

分享 tab 程序啟動 下拉刷新控件 ram pen address register 單例

在介紹小項目之前,在此說明一下此代碼並非本人所寫,我只是隨筆的整理者。

在介紹之前先展現一下效果圖。

技術分享

看過效果圖大家應該很熟悉了,就是餓了麽的一個界面而已,值得註意的是,實現時並沒有采用本地連接,而是實打實的網絡連接。看一下文件架構。

技術分享

這一采用的是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-小項目(餓了麽 網絡部分 簡單實現)