首页 >> 大全

AFNetworking速成教程(1)

2023-06-22 大全 53 作者:考证青年

这篇文章还可以在这里找到英语

Learn how to use : an easy to use API for iOS!

本文是由 iOS 小组成员Scott 撰写,他是一个基于位置动态加载( )的软件公司(专业的混合定位)的共同创办人。

网络 — 你的程序离开了它就不能生存下去!苹果的 中的又非常难以理解, 不过这里有一个可以使用的替代品:.

非常受开发者欢迎 – 它赢得了我们读者的青睐:2012年最佳的iOS 奖(2012 Best iOS Award.) 所以现在我就写这篇文章来向你介绍如何在程序中有效的使用它。

包括了所有你需要与在线资源交互的内容,从web 到文件下载。当你的程序在下载一个大文件期间,还能确保你的UI是可以响应的。

本文将介绍框架主要的组成部分。一路上,你将使用World 提供的咨询(Feeds)来创建一个天气()程序。刚开始使用的天气数据是静态的,不过在学完本文内容之后,程序将连接到实时的天气咨询。

今日预计:一个很酷的开发者学习所有关于知识,并在他的程序中使用。我们开始忙活吧!

开始

首先来这里(here)下载本文的启动项目。这个工程提供了一个基本的UI —相关代码还没有添加。

打开.文件,将看到3个view :

从左到右,分别是:

生成并运行项目,你将看到相关的UI出现,但是什么都没有实现!因为程序需要从网络中获取到所需要的数据,而相关代码还没有添加。这也是本文中你将要实现的!

首先,你需要将 框架包含到工程中。如果你还没有的话,在这里下载最新的版本:.

当你解压出下载的文件后,你将看到其中有一个子文件夹,里面全是.h 和 .m 文件, 如下高亮显示的:

将 拖拽到Xcode工程中.

当出现了添加文件的选项时,确保勾选上 Copy items into group’s (if )和 for any added .

要完成相关配置,请在工程的 Files群组中打开预编译头文件-.pch. 然后在别的后面添加如下一行代码:

#import "AFNetworking.h"

将添加到预编译头文件,意味着这个框架会被自动的添加到工程的所有源代码文件中。

很容易,不是吗?现在你已经准备好“天气”程序代码了!

操作JSON

通过网络来加载和处理结构化的数据是非常智能的,普通的HTTP请求也一样。尤其是它支持JSON, XML 和 Lists ().

你可以下载一些JSON数据,然后用自己的解析器来解析,但这何必呢?通过就可以完成这些操作!

首先,你需要测试脚本(数据)所需的一个基本URL。将下面的这个静态声明到 r.m顶部,也就是所有#下面:

static NSString *const BaseURLString = @"http://www.raywenderlich.com/downloads/weather_sample/";

这个URL是一个非常简单的“web ”,在本文中我特意为你创建的。如果你想知道它看起来是什么样,可以来这里下载代码: the .

这个web 以3种不同的格式(JSON, XML 和 PLIST)返回天气数据。你可以使用下面的这些URL来看看返回的数据:

第一个数据格式使用的是JSON. JSON 是一种常见的派生类对象格式。看起来如下:

{"data": {"current_condition": [{"cloudcover": "16","humidity": "59","observation_time": "09:09 PM",}]}
}

注意:如果你想要结更多关于JSON内容,请参考: with JSON in iOS 5 .

当用户点击程序中的JSON按钮时,你希望对从服务中获得的JSON数据进行加载并处理。在r.m中,找到:方法 (现在应该是空的) ,并用下面的代码替换:

- (IBAction)jsonTapped:(id)sender {// 1NSString *weatherUrl = [NSString stringWithFormat:@"%@weather.php?format=json", BaseURLString];NSURL *url = [NSURL URLWithString:weatherUrl];NSURLRequest *request = [NSURLRequest requestWithURL:url];// 2AFJSONRequestOperation *operation =[AFJSONRequestOperation JSONRequestOperationWithRequest:request// 3success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {self.weather  = (NSDictionary *)JSON;self.title = @"JSON Retrieved";[self.tableView reloadData];}// 4failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather"message:[NSString stringWithFormat:@"%@",error]delegate:nilcancelButtonTitle:@"OK" otherButtonTitles:nil];[av show];}];// 5[operation start];
}

这是你的第一个代码!因此,这看起来是全新的,我将对这个方法中代码进行介绍。

根据基本的URL构造出完整的一个URL。然后通过这个完整的URL获得一个NSURL对象,然后根据这个url获得一个. on 是一个功能完整的类(all-in-one)— 整合了从网络中获取数据并对JSON进行解析。 当请求成功,则运行成功块( block)。在本示例中,把解析出来的天气数据从JSON变量转换为一个字典(),并将其存储在属性中. 如果运行出问题了,则运行失败块( block),比如网络不可用。如果 block被调用了,将会通过提示框显示出错误信息。

如上所示,的使用非常简单。如果要用苹果提供的APIs(如)来实现同样的功能(下载和解析JSON数据),则需要许多代码才能做到。

现在天气数据已经存在于self.中,你需要将其显示出来。找到:n:方法,并用下面的代码替换:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{// Return the number of rows in the section.if(!self.weather)return 0;switch (section) {case 0:{return 1;}case 1:{NSArray *upcomingWeather = [self.weather upcomingWeather];return [upcomingWeather count];}default:return 0;}
}

table view有两个:第一个用来显示当前天气,第二个用来显示未来的天气。

等一分钟,你可能正在思考。这里的[self. ]是什么?如果self.是一个普通的, 它是怎么知道 “” 是什么呢?

为了更容易的解析数据,在工程中,有一对es:

这些添加了一些方便的方法,通过这些方法可以很方便的对字典中的数据元素进行访问。这样你就可以专注于网络部分,而不是中数据的访问。对吧?

回到r.m, 找到:h:方法,并用下面的实现替换:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{static NSString *CellIdentifier = @"WeatherCell";UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];NSDictionary *daysWeather;switch (indexPath.section) {case 0:{daysWeather = [self.weather currentCondition];break;}case 1:{NSArray *upcomingWeather = [self.weather upcomingWeather];daysWeather = [upcomingWeather objectAtIndex:indexPath.row];}default:break;}cell.textLabel.text = [daysWeather weatherDescription];// maybe some code will be added here later...return cell;
}

跟:n: 方法一样,在这里使用了便利的 来获得数据。当前天的天气是一个字典,而未来几日的天气则存储在一个数组中。

生成并运行工程,然后点击JSON按钮. 这将会动态的获得一个对象, 并看到如下画面内容:

JSON 操作成功! 操作 Lists()

lists (或简称为 ) 是以确定的格式(苹果定义的)构成的XML文件。苹果一般将用在用户设置中。看起来如下:

datacurrent_conditioncloudcover16humidity59

上面的意思是:

现在是时候加载plist版本的天气数据了!找到:方法,并用下面的实现替换:

 -(IBAction)plistTapped:(id)sender{NSString *weatherUrl = [NSString stringWithFormat:@"%@weather.php?format=plist",BaseURLString];NSURL *url = [NSURL URLWithString:weatherUrl];NSURLRequest *request = [NSURLRequest requestWithURL:url];AFPropertyListRequestOperation *operation =[AFPropertyListRequestOperation propertyListRequestOperationWithRequest:requestsuccess:^(NSURLRequest *request, NSHTTPURLResponse *response, id propertyList) {self.weather  = (NSDictionary *)propertyList;self.title = @"PLIST Retrieved";[self.tableView reloadData];}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id propertyList) {UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather"message:[NSString stringWithFormat:@"%@",error]delegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil];[av show];}];[operation start];
}

注意到,上面的代码几乎与JSON版的一致,只不过将操作()的类型从 修改为 ion. 这非常的整齐:你才程序只需要修改一丁点代码就可以接收JSON或plist格式的数据了!

生成并运行工程,然后点击PLIST按钮。将看到如下内容:

如果你需要重置所有的内容,以重新开始操作,导航栏顶部的Clear按钮可以清除掉title和table view中的数据。 操作XML

处理JSON和plist的解析使用的是类似的方法,并不需要花费太多功夫,而处理XML则要稍微复杂一点。下面,就根据XML咨询构造一个天气字典()。

iOS提供了一个帮助类: (如果你想了解更多内容,请看这里的链接:SAX ).

还是在文件r.m, 找到:方法,并用下面的实现替换:

- (IBAction)xmlTapped:(id)sender{NSString *weatherUrl = [NSString stringWithFormat:@"%@weather.php?format=xml",BaseURLString];NSURL *url = [NSURL URLWithString:weatherUrl];NSURLRequest *request = [NSURLRequest requestWithURL:url];AFXMLRequestOperation *operation =[AFXMLRequestOperation XMLParserRequestOperationWithRequest:requestsuccess:^(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser) {//self.xmlWeather = [NSMutableDictionary dictionary];XMLParser.delegate = self;[XMLParser setShouldProcessNamespaces:YES];[XMLParser parse];}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLParser *XMLParser) {UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather"message:[NSString stringWithFormat:@"%@",error]delegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil];[av show];}];[operation start];
}

到现在为止,这看起来跟之前处理JSON和plist很类似。最大的改动就是在成功块( block)中, 在这里不会传递给你一个预处理好的对象. 而是n实例化的对象,这个对象将用来处理繁重的XML解析任务。

对象有一组方法是你需要实现的 — 用来获得XML数据。注意,在上面的代码中我将的设置为self, 因此r将用来处理XML的解析任务。

首先,更新一下r.h并修改一下类声明,如下所示:

@interface WTTableViewController : UITableViewController<NSXMLParserDelegate>

上面代码的意思是这个类将实现(遵循)协议. 下一步将下面的方法声明添加到@后面:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
-(void) parserDidEndDocument:(NSXMLParser *)parser;

为了支持资讯的解析,还需要一些属性来存储相关的数据。将下面的代码添加到@后面:

@property(strong) NSMutableDictionary *xmlWeather; //package containing the complete response
@property(strong) NSMutableDictionary *currentDictionary; //current section being parsed
@property(strong) NSString *previousElementName;
@property(strong) NSString *elementName;
@property(strong) NSMutableString *outstring;

接着打开r.m,现在你需要一个一个的实现上面所说的几个方法。将下面这个方法粘贴到实现文件中:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict  {self.previousElementName = self.elementName;if (qName) {self.elementName = qName;}if([qName isEqualToString:@"current_condition"]){self.currentDictionary = [NSMutableDictionary dictionary];}else if([qName isEqualToString:@"weather"]){self.currentDictionary = [NSMutableDictionary dictionary];}else if([qName isEqualToString:@"request"]){self.currentDictionary = [NSMutableDictionary dictionary];}self.outstring = [NSMutableString string];
}

当发现了新的元素开始标签时,会调用上面这个方法。在这个方法中,在构造一个新字典用来存储赋值给属性之前,首先保存住上一个元素名称。还要将重置一下,这个字符串用来构造XML标签中的数据。

然后将下面这个方法粘贴到上一个方法的后面:

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {if (!self.elementName){return;}[self.outstring appendFormat:@"%@", string];
}

如名字一样,当在一个XML标签中发现了字符数据,会调用这个方法。该方法将字符数据追加到属性中,当XML标签结束的时候,这个会被处理。

继续,将下面这个方法粘贴到上一个方法的后面:

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {// 1if([qName isEqualToString:@"current_condition"] ||[qName isEqualToString:@"request"]){[self.xmlWeather setObject:[NSArray arrayWithObject:self.currentDictionary] forKey:qName];self.currentDictionary = nil;}// 2else if([qName isEqualToString:@"weather"]){// Initalise the list of weather items if it dosnt existNSMutableArray *array = [self.xmlWeather objectForKey:@"weather"];if(!array)array = [NSMutableArray array];[array addObject:self.currentDictionary];[self.xmlWeather setObject:array forKey:@"weather"];self.currentDictionary = nil;}// 3else if([qName isEqualToString:@"value"]){//Ignore value tags they only appear in the two conditions below}// 4else if([qName isEqualToString:@"weatherDesc"] ||[qName isEqualToString:@"weatherIconUrl"]){[self.currentDictionary setObject:[NSArray arrayWithObject:[NSDictionary dictionaryWithObject:self.outstring forKey:@"value"]] forKey:qName];}// 5else {[self.currentDictionary setObject:self.outstring forKey:qName];}self.elementName = nil;
}

当检测到元素的结束标签时,会调用上面这个方法。在这个方法中,会查找一些标签:

元素表示获得了一个今天的天气。会把今天的天气直接添加到字典中。 元素表示获得了随后一天的天气。今天的天气只有一个,而后续的天气有多个,所以在此,将后续天气添加到一个数组中。 value标签出现在别的标签中,所以这里可以忽略掉这个标签。 和元素的值在存储之前,需要需要被放入一个数组中 — 这里的结构是为了与JSON和plist版本天气咨询格式相匹配。 所有其它元素都是按照原样(as-is)进行存储的。

下面是最后一个方法!将下面这个方法粘贴到上一个方法的后面:

-(void) parserDidEndDocument:(NSXMLParser *)parser {self.weather = [NSDictionary dictionaryWithObject:self.xmlWeather forKey:@"data"];self.title = @"XML Retrieved";[self.tableView reloadData];
}

当解析到的尾部时,会调用这个方法。在此,字典已经构造完毕,table view可以重新加载了。

在上面代码中将添加到一个字典中,看起来是冗余的, 不过这样可以确保与JSON和plist版本的格式完全匹配。这样所有的3种数据格式(JSON, plist和XML)都能够用相同的代码来显示!

现在所有的方法和属性都搞定了,找到 :方法,并取消注释成功块( block)中的一行代码:

-(IBAction)xmlTapped:(id)sender{...success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser) {// the line below used to be commented outself.xmlWeather = [NSMutableDictionary dictionary];XMLParser.delegate = self;...
}

生成和运行工程,然后点击XML按钮,将看到如下内容:

一个小的天气程序

嗯, 上面的这个程序看起来体验不太友好,有点像整周都是阴雨天。如何让table view中的天气信息体验更好点呢?

再仔细看看之前的JSON格式数据:JSON from ,你会看到每个天气项里面都有一个图片URLs。将这些天气图片显示到每个table view cell中,这样程序看起来会更有意思。

给添加了一个,让图片能够异步加载,也就是说当图片在后台下载的时候,程序的UI界面仍然能够响应。为了使用这个功能,首先需要将这个 到r.m文件的顶部:

#import "UIImageView+AFNetworking.h"

找到tableView:cellForRowAtIndexPath: 方法,并将下面的代码粘贴到最后的return cell; 代码上上面(这里应该有一个注释标记)

__weak UITableViewCell *weakCell = cell;[cell.imageView setImageWithURLRequest:[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:daysWeather.weatherIconURL]]placeholderImage:[UIImage imageNamed:@"placeholder.png"]success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){weakCell.imageView.image = image;//only required if no placeholder is set to force the imageview on the cell to be laid out to house the new image.//if(weakCell.imageView.frame.size.height==0 || weakCell.imageView.frame.size.width==0 ){[weakCell setNeedsLayout];//}}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){}];

首先创建一个弱引用(weak)的cell,这样就可以在block中使用这个cell。如果你直接访问cell变量,Xcode会提示一个关于循环和内存泄露的警告。

+ 定义了一个st…方法. 这个方法的参数包括:一个指向图片URL的请求,一个占位符图片,一个 block和一个 block。

当cell首次被创建的时候,cell中的将显示一个占位符图片,直到真正的图片被下载完成。在这里你需要确保占位符的图片与实际图片尺寸大小相同。

如果尺寸不相同的话,你可以在 block中调用cell的方法. 上面代码中对两行代码进行了注释,这是因为这里的占位符图片尺寸正好合适,留着注释,可能在别的程序中需要用到。

现在生成并运行工程,然后点击之前添加的3个操作中的任意一个,将看到如下内容:

很好! 异步加载图片从来没有这么简单过。 一个类

到现在你已经使用类似on这样的类创建了一次性的HTTP请求。另外,较低级的类是用来访问单个的web 终端。 对这个一般是给它设置一个基本的URL,然后用进行多个请求(而不是像之前的那样,每次请求的时候,都创建一个)。

同样为编码参数、处理表单请求body的构造、管理请求操作和批次入队列操作提供了很强的灵活性,它还处理了整套 (GET, POST, PUT, 和 ), 下面我们就来试试最常用的两个:GET 和 POST.

注意:对REST, GET和POST不清楚?看看这里比较有趣的介绍 – 我如何给妻子解释REST(How I REST to My Wife.)

在r.h顶部将类声明按照如下修改:

@interface WTTableViewController : UITableViewController

在r.m中,找到:方法,并用下面的实现替换:

- (IBAction)httpClientTapped:(id)sender {UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"AFHTTPClient" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"HTTP POST",@"HTTP GET", nil];[actionSheet showFromBarButtonItem:sender animated:YES];
}

上面的方法会弹出一个 sheet,用以选择GET和POST请求。粘贴如下代码以实现 sheet中按钮对应的操作:

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{// 1NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:BaseURLString]];NSDictionary *parameters = [NSDictionary dictionaryWithObject:@"json" forKey:@"format"];// 2AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];[client registerHTTPOperationClass:[AFJSONRequestOperation class]];[client setDefaultHeader:@"Accept" value:@"application/json"];// 3if (buttonIndex==0) {[client postPath:@"weather.php"parameters:parameterssuccess:^(AFHTTPRequestOperation *operation, id responseObject) {self.weather = responseObject;self.title = @"HTTP POST";[self.tableView reloadData];}failure:^(AFHTTPRequestOperation *operation, NSError *error) {UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather"message:[NSString stringWithFormat:@"%@",error]delegate:nilcancelButtonTitle:@"OK" otherButtonTitles:nil];[av show];}];}// 4else if (buttonIndex==1) {[client getPath:@"weather.php"parameters:parameterssuccess:^(AFHTTPRequestOperation *operation, id responseObject) {self.weather = responseObject;self.title = @"HTTP GET";[self.tableView reloadData];}failure:^(AFHTTPRequestOperation *operation, NSError *error) {UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Weather"message:[NSString stringWithFormat:@"%@",error]delegate:nilcancelButtonTitle:@"OK" otherButtonTitles:nil];[av show];}];}
}

上面的代码作用如下:

构建一个,以及一个参数字典,并将这两个变量传给. 将on注册为HTTP的操作, 这样就可以跟之前的示例一样,可以获得解析好的JSON数据。 做了一个GET请求,这个请求有一对block:和。 POST请求跟GET一样。

在这里,将请求一个JSON回应,当然也可以使用之前讨论过的另外两种格式来代替JSON。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了