在介紹小項目之前,在此說明一下此代碼並非本人所寫,我只是隨筆的整理者。
在介紹之前先展現一下效果圖。
看過效果圖大家應該很熟悉了,就是餓了么的一個界面而已,值得注意的是,實現時並沒有采用本地連接,而是實打實的網絡連接。看一下文件架構。
這一采用的是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
就這樣一個小項目就這么的完成了。