前言
因为是重制的客户端,作者博客中写到的很详细的内容相同的这里不会再写,但会给出提示,对照查看。
目录结构
由于原作没有按照分层架构设计,所以这里我按照自己想法进行分层架构设计的,不过本来我也是全写在一个文件中的,但是文件过于冗长层次也不清晰所以拆分了两个模块下来,真的是很难拆,要改动很多,所以只拆了两个下来放到BusinessLayer中了。
准备工作
- 首先作者提供的 美团api 地址过久已经失效,故用了 百度糯米api
- 选择城市生成url就可以拿来使用了
- 注意像北京上海南京等大城市会取得超大量的数据,直接把地址粘贴到下载工具下载保存成.xml形式,会有七十多兆的大小,而像香港澳门只有200多K
- 用到的第三方框架有:
- MBProcessHUD
- ASIHttpRequest
- TBXML (原用的KissXML,但我认为TBXML解析更快,而且根本不需要写入的功能,所以更换成更轻量的TBXML,但同样都是基于dom的解析框架。)
- AQGridView
- TSPopover (这个框架是我加上去的,因为原app选择城市是读取城市api直接通过segue跳转到UITableView了,而我并没有找到糯米关于城市的api,故只加了一共3个城市,用了TSPopover更美观的显示出来。)
Let’s do it!
框架导入
仿佛回到了刚刚重制app的时候,当然我不用迷茫,作者已经告诉了我首先要做什么,当然是导入框架!
首先按照iPhone团购信息客户端的开发 (一)做完已经建立了程序主视图并且导入了ASIHttpRequest框架。
当然不明不白的导入一个不知道起什么作用的框架肯定不行,翻阅资料后确实觉得这个框架挺不错,至少比直接调用系统提供的api简单的多,并且还封装了其它有意思的事情。
导入第一个框架之后,接下来请看iPhone团购信息客户端的开发 (二)
这里告诉你了怎样添加KissXML,MBProgressHUD,AQridView这三个框架,但是注意KissXML请不要导入,导入TBXML,和导入KissXML一样简单,并不需要其它的框架支持。同样的,导入TSPopover框架。
- 如果导入后发现有错误,请加入pch文件
PrefixHeader.pch
#ifndef PrefixHeader_pch
#define PrefixHeader_pch
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#define ARC_ENABLED
#endif
如不知怎样加入pch文件请移步:Xcode7 建立 pch 文件 (预编译头文件)
- 遇到缺少GHUnitIOS.framework怎么办?
到github下载gh-unit看Readme进行编译得到framework。
开始写!
OK到了写代码的时候,我也是从主视图的第1个开始写的。
不像原作那样一步步的制作过程都贴上来,程序已经complete,这里只是描述一下制作过程,代码中注释的很详细了,如果有不明白的地方可以贴上来共同探讨 :)
上面这张图片标号分别对应:
1. 主视图
2. 商品详情视图
3. 商家所在地图位置
4. 关于
因为并没有用到数据库什么的,我在PersistenceLayer只存放了地图上标注气泡信息类,可以先不从这里写起,直接从BusinessLogicLayer开始。
首先从XML的解析开始
XMLParser.h
#import <Foundation/Foundation.h>
#import "TBXML.h"
@interface XMLParser : NSObject
@property (nonatomic, strong) NSMutableArray *arrays;
@property (nonatomic, readonly) NSString *urlChildOfRootElement;
@property (nonatomic, readonly) NSString *dataChildOfUrlElement;
@property (nonatomic, readonly) NSString *displayChildOfDataElement;
@property (nonatomic, readonly) NSString *tb_siteUrl;
@property (nonatomic, readonly) NSString *tb_title;
@property (nonatomic, readonly) NSString *tb_image;
@property (nonatomic, readonly) NSString *tb_price;
@property (nonatomic, readonly) NSString *tb_shops;
@property (nonatomic, readonly) NSString *tb_shop;
@property (nonatomic, readonly) NSString *tb_shopName;
@property (nonatomic, readonly) NSString *tb_address;
@property (nonatomic, readonly) NSString *tb_longitude;
@property (nonatomic, readonly) NSString *tb_latitude;
- (NSMutableArray *)xmlParser:(NSData *)data;
@end
之所以在这里面写了那么多public属性,是方便主视图的读取,或许这样太乱了,我根本就该写一个类来进行封装!
XMLParser.m
#import "XMLParser.h"
@interface XMLParser () {
TBXML *tbxml;
}
@end
@implementation XMLParser
- (id)init {
if (self = [super init]) {
_urlChildOfRootElement = @"url";
_dataChildOfUrlElement = @"data";
_displayChildOfDataElement = @"display";
_tb_siteUrl = @"wapGoodsURL";
_tb_title = @"title";
_tb_image = @"image";
_tb_price = @"price";
_tb_shops = @"shops";
_tb_shop = @"shop";
_tb_shopName = @"name";
_tb_address = @"addr";
_tb_longitude = @"longitude";
_tb_latitude = @"latitude";
}
return self;
}
- (NSMutableArray *)xmlParser:(NSData *)data {
NSError *err;
_arrays = [NSMutableArray array];
//载入XML
if (data) {
tbxml = [[TBXML alloc] initWithXMLData:data error:&err];
} else {
tbxml = [[TBXML alloc] initWithXMLFile:@"dailydeal.xml" error:&err];
}
//读取根点播洋葱
TBXMLElement *root = tbxml.rootXMLElement;
if (root) {
TBXMLElement *url = [TBXML childElementNamed:_urlChildOfRootElement parentElement:root error:&err];
while (url != nil) {
TBXMLElement *data = [TBXML childElementNamed:_dataChildOfUrlElement parentElement:url error:&err];
if (data) {
TBXMLElement *display = [TBXML childElementNamed:_displayChildOfDataElement parentElement:data error:&err];
if (display) {
NSMutableDictionary *dict = [NSMutableDictionary new];
TBXMLElement *siteurl = [TBXML childElementNamed:_tb_siteUrl parentElement:display];
if (siteurl) {
NSString *siteurlString = [TBXML textForElement:siteurl];
[dict setValue:siteurlString forKey:_tb_siteUrl];
}
TBXMLElement *title = [TBXML childElementNamed:_tb_title parentElement:display];
if (title) {
NSString *titleString = [TBXML textForElement:title];
[dict setValue:titleString forKey:_tb_title];
}
TBXMLElement *image = [TBXML childElementNamed:_tb_image parentElement:display];
if (image) {
//此时得到的URL并不能直接解析成图片,需要截取
NSString *imageUrl = [TBXML textForElement:image];
NSString *tempStr = @"http://e.hiphotos.baidu.com/";
NSRange range = [imageUrl rangeOfString:tempStr];
NSUInteger location = range.location;
//warning 这段代码太冗余了
if (location > 1000) {
tempStr = @"http://S1.nuomi.bdimg.com/";
range = [imageUrl rangeOfString:tempStr];
location = range.location;
if (location > 1000) {
tempStr = @"http://S2.nuomi.bdimg.com/";
range = [imageUrl rangeOfString:tempStr];
location = range.location;
if (location > 1000) {
tempStr = @"http://S0.nuomi.bdimg.com/";
range = [imageUrl rangeOfString:tempStr];
location = range.location;
}
}
}
imageUrl = [imageUrl substringFromIndex:location];
[dict setValue:imageUrl forKey:_tb_image];
}
TBXMLElement *price = [TBXML childElementNamed:_tb_price parentElement:display];
if (price) {
NSString *priceString = @"¥";
priceString = [priceString stringByAppendingString:[TBXML textForElement:price]];
[dict setValue:priceString forKey:_tb_price];
}
//店铺字典
NSMutableDictionary *dictOfShops = [NSMutableDictionary new];
TBXMLElement *shops = [TBXML childElementNamed:_tb_shops parentElement:display];
if (shops) {
TBXMLElement *shop = [TBXML childElementNamed:_tb_shop parentElement:shops];
if (shop) {
TBXMLElement *shopName = [TBXML childElementNamed:_tb_shopName parentElement:shop];
if (shopName) {
NSString *shopNameStr = [TBXML textForElement:shopName];
[dictOfShops setValue:shopNameStr forKey:_tb_shopName];
}
TBXMLElement *shopAddress = [TBXML childElementNamed:_tb_address parentElement:shop];
if (shopAddress) {
NSString *addressStr = [TBXML textForElement:shopAddress];
[dictOfShops setValue:addressStr forKey:_tb_address];
}
TBXMLElement *shopLongitude = [TBXML childElementNamed:_tb_longitude parentElement:shop];
if (shopLongitude) {
NSString *longitudeStr = [TBXML textForElement:shopLongitude];
[dictOfShops setValue:longitudeStr forKey:_tb_longitude];
}
TBXMLElement *shopLatitude = [TBXML childElementNamed:_tb_latitude parentElement:shop];
if (shopLatitude) {
NSString *latitudeStr = [TBXML textForElement:shopLatitude];
[dictOfShops setValue:latitudeStr forKey:_tb_latitude];
}
}
}
[dict setValue:dictOfShops forKey:_tb_shop];
//数据添加到数组
[_arrays addObject:dict];
//移动到下一个节点开始解析
url = [TBXML nextSiblingNamed:_urlChildOfRootElement searchFromElement:url];
}
}
}
}
return _arrays;
}
@end
这一段代码是通过TBXML进行的解析,有些地方也比较冗余,或许有更简洁的语法实现,唉,懒得思考……
这里你看到假如data为nil,我就读取我里面存在的xml文件,这是下载好的南京市的api截取了前20条信息,不然通过网络读取的话网速不好要下载好长时间,当然香港澳门这两地API长度很小可以直接通过网络读取,不过当网速很好的时候你就看不到HUD进度条的动画效果了。
再一个就是图片的处理,把从xml读取到的image地址进行网络请求多线程加载图片。
PicProcessor.h
#import <Foundation/Foundation.h>
#import "ASIHTTPRequest.h"
@class FirstViewController;
@interface PicProcessor : NSObject
@property (nonatomic, strong) NSMutableDictionary *cachedImage;
@property (nonatomic, strong) NSOperationQueue *queue;
- (UIImage *)cachedImageForUrl:(NSURL *)url;
@end
PicProcessor.m
//
// PicProcessor.m
// 今日糯米团
//
// Created by Sunny on 12/12/15.
// Copyright © 2015 IOSDevelopeGuid. All rights reserved.
//
#import "PicProcessor.h"
@implementation PicProcessor
//需要在这里进行初始化
- (id)init {
if (self = [super init]) {
_queue = [[NSOperationQueue alloc] init];
_cachedImage = [NSMutableDictionary dictionary];
}
return self;
}
//下载缓存图片处理
- (UIImage *)cachedImageForUrl:(NSURL *)url {
id cachedObject = [self.cachedImage objectForKey:url];
if (!cachedObject) {
[self.cachedImage setObject:@"Loading..." forKey:url]; //添加占位符
ASIHTTPRequest *picRequest = [ASIHTTPRequest requestWithURL:url];
picRequest.delegate = self;
picRequest.didFinishSelector = @selector(didFinishRequestImage:);
picRequest.didFailSelector = @selector(didFailRequestImage:);
//加入多线程进行处理
[_queue addOperation:picRequest];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
} else if (![cachedObject isKindOfClass:[UIImage class]]) {
cachedObject = nil;
}
return cachedObject;
}
//下载图片成功
- (void)didFinishRequestImage:(ASIHTTPRequest *)request {
NSData *imageData = [request responseData];
UIImage *image = [UIImage imageWithData:imageData];
if (image) {
[_cachedImage setObject:image forKey:request.url];
[[NSNotificationCenter defaultCenter] postNotificationName:@"gridReload" object:nil];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
//下载图片失败
- (void)didFailRequestImage:(ASIHTTPRequest *)request {
//从当前缓存中移除
[_cachedImage removeObjectForKey:request.url];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
@end
好了,BusinessLogicLayer主要就是写这两块,也是我费了一段时间拆下来的两块,至于其它的第三方框架和需求库我也放到里面了。