這筆記來自於 CS 193P iPhone Application Development 2010 Winter 課程 - 9. Data in Your iPhone App (February 2, 2010) ,另外,關於申請使用 Flickr API 的部份,可以參考 [PHP] 申請使用 Flickr API 教學筆記 - 以 flickr.photos.search 為例 ,另外,此例還會用到 UITableView ,可參考 iPhone 開發教學 - 使用 UITableView & UITableViewController 提供表單服務 。
越來越多網路服務提供 API 讓其他平台可以更簡單地連結到,並且常常使用 JSON 的資料格式來提供多元且方便的 UI 應用,在此,挑選 Flickr API 當作範例,透過對 Flickr 搜尋特定 Tag 後,把找到的圖片資訊利用 UITableView 顯示出來!
此篇目的:
- 使用 JSON 的方式
- 使用 NSDictionary 的 Key-Value Pair 來製作 REQUEST URL(如 PHP - http_build_query)
流程:
- 下載 JSON (json-framework)
- This framework implements a strict JSON parser and generator in Objective-C.
- [Xcode]->[Create a new Xcode project]->[iPhone OS]->[Application]->[Window-based Application]-> 此例以 MyJSONTest 為例
- [Xcode]->[File]->[Cocoa Touch Class]->[UIViewController subclass] (勾選 UITableViewController subclass) -> 此例以 MyTableList 為例
- 將下載回來的 JSON 打開,並把其中一個目錄 JSON 移到 Project 適當的位置
程式碼:
MyJSONTestAppDelegate.h
#import <UIKit/UIKit.h>
#import "MyTableList.h"
@interface MyJSONTestAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyTableList *myList;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
#import "MyTableList.h"
@interface MyJSONTestAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyTableList *myList;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
MyJSONTestAppDelegate.m
#import "MyJSONTestAppDelegate.h"
@implementation MyJSONTestAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
myList = [[MyTableList alloc] init];
[window addSubview:myList.view];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)dealloc {
[myList release];
[window release];
[super dealloc];
}
@end
@implementation MyJSONTestAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
myList = [[MyTableList alloc] init];
[window addSubview:myList.view];
// Override point for customization after application launch
[window makeKeyAndVisible];
}
- (void)dealloc {
[myList release];
[window release];
[super dealloc];
}
@end
MyTableList.h
#import <UIKit/UIKit.h>
#define FLICKR_API_KEY @"YOUR_API_KEY"
@interface MyTableList : UITableViewController {
NSMutableArray *dataSource;
}
@end
#define FLICKR_API_KEY @"YOUR_API_KEY"
@interface MyTableList : UITableViewController {
NSMutableArray *dataSource;
}
@end
MyTableList.m
#import "MyTableList.h"
#import "JSON.h"
@implementation MyTableList
- (NSString*)http_build_query:(NSString *)targetUrl options:(NSMutableDictionary *) args {
NSMutableString * url = [[NSMutableString alloc] initWithString:targetUrl];
NSStringEncoding encoding = NSUTF8StringEncoding;
BOOL first_in = YES;
for ( id key in [args allKeys] ) {
if (first_in) {
first_in = NO;
[url appendFormat:@"%@=%@",
[[NSString stringWithFormat:@"%@", key] stringByAddingPercentEscapesUsingEncoding:encoding],
[[NSString stringWithFormat:@"%@",[args objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:encoding]
];
} else {
[url appendFormat:@"&%@=%@",
[[NSString stringWithFormat:@"%@", key] stringByAddingPercentEscapesUsingEncoding:encoding],
[[NSString stringWithFormat:@"%@",[args objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:encoding]
];
}
}
return [url autorelease];
}
- (void)viewDidLoad {
[super viewDidLoad];
dataSource = [[NSMutableArray alloc] init];
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
[data setValue:@"flickr.photos.search" forKey:@"method"];
[data setValue:@"json" forKey:@"format"];
[data setValue:@"1" forKey:@"nojsoncallback"];
[data setValue:FLICKR_API_KEY forKey:@"api_key"];
[data setValue:@"10" forKey:@"per_page"];
[data setValue:@"台灣 正妹" forKey:@"tags"];
NSURL *target = [[NSURL alloc] initWithString:[self http_build_query:@"http://api.flickr.com/services/rest/?" options:data]];
//NSLog( [NSString stringWithFormat:@"%@" , target] );
NSString *jsonResult = [NSString stringWithContentsOfURL:target encoding:NSUTF8StringEncoding error:nil];
//NSLog( jsonResult );
//SBJSON *parser = [[SBJSON alloc] init];
//NSDictionary *formatedData = [parser objectWithString:result error:nil];
NSDictionary *result = [jsonResult JSONValue];
//NSLog( [NSString stringWithFormat:@"JSONParsing:%d", [result count] ]);
//NSLog( [NSString stringWithFormat:@"Photos:%d", [[result objectForKey:@"photos"] count] ]);
//NSLog( [NSString stringWithFormat:@"PhotosArray:%d", [[[result objectForKey:@"photos"] valueForKeyPath:@"photo"] count]]);
// http://www.flickr.com/services/api/misc.urls.html
id obj;
for( NSDictionary *photo in [[result objectForKey:@"photos"] valueForKeyPath:@"photo"] ) {
//NSLog( [NSString stringWithFormat:@"Title:%@", [photo objectForKey:@"title"] ] );
//NSLog( [NSString stringWithFormat:@"URL:%@", [NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_s.jpg", [photo objectForKey:@"farm"] , [photo objectForKey:@"server"] , [photo objectForKey:@"id"] , [photo objectForKey:@"secret"]] ] );
[dataSource addObject:
[[NSDictionary alloc] initWithObjectsAndKeys:
( ( (obj = [photo objectForKey:@"title"]) && [obj length] > 0 ) ? obj : @"Untitled" ) ,
@"title" ,
[NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_s.jpg", [photo objectForKey:@"farm"] , [photo objectForKey:@"server"] , [photo objectForKey:@"id"] , [photo objectForKey:@"secret"]],
@"url" ,
nil
]
];
}
[target release];
[data release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//NSLog( [NSString stringWithFormat:@"Cnt:%d" , [dataSource count]] );
return [dataSource count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
cell.textLabel.text = [[dataSource objectAtIndex:indexPath.row] objectForKey:@"title"];
//NSLog( [NSString stringWithFormat:@"%@", [[dataSource objectAtIndex:indexPath.row] objectForKey:@"title"]] );
cell.imageView.image = [UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
[[dataSource objectAtIndex:indexPath.row] objectForKey:@"url"]
]
]
];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
// AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
// [self.navigationController pushViewController:anotherViewController];
// [anotherViewController release];
}
- (void)dealloc {
[dataSource release];
[super dealloc];
}
@end
#import "JSON.h"
@implementation MyTableList
- (NSString*)http_build_query:(NSString *)targetUrl options:(NSMutableDictionary *) args {
NSMutableString * url = [[NSMutableString alloc] initWithString:targetUrl];
NSStringEncoding encoding = NSUTF8StringEncoding;
BOOL first_in = YES;
for ( id key in [args allKeys] ) {
if (first_in) {
first_in = NO;
[url appendFormat:@"%@=%@",
[[NSString stringWithFormat:@"%@", key] stringByAddingPercentEscapesUsingEncoding:encoding],
[[NSString stringWithFormat:@"%@",[args objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:encoding]
];
} else {
[url appendFormat:@"&%@=%@",
[[NSString stringWithFormat:@"%@", key] stringByAddingPercentEscapesUsingEncoding:encoding],
[[NSString stringWithFormat:@"%@",[args objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:encoding]
];
}
}
return [url autorelease];
}
- (void)viewDidLoad {
[super viewDidLoad];
dataSource = [[NSMutableArray alloc] init];
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
[data setValue:@"flickr.photos.search" forKey:@"method"];
[data setValue:@"json" forKey:@"format"];
[data setValue:@"1" forKey:@"nojsoncallback"];
[data setValue:FLICKR_API_KEY forKey:@"api_key"];
[data setValue:@"10" forKey:@"per_page"];
[data setValue:@"台灣 正妹" forKey:@"tags"];
NSURL *target = [[NSURL alloc] initWithString:[self http_build_query:@"http://api.flickr.com/services/rest/?" options:data]];
//NSLog( [NSString stringWithFormat:@"%@" , target] );
NSString *jsonResult = [NSString stringWithContentsOfURL:target encoding:NSUTF8StringEncoding error:nil];
//NSLog( jsonResult );
//SBJSON *parser = [[SBJSON alloc] init];
//NSDictionary *formatedData = [parser objectWithString:result error:nil];
NSDictionary *result = [jsonResult JSONValue];
//NSLog( [NSString stringWithFormat:@"JSONParsing:%d", [result count] ]);
//NSLog( [NSString stringWithFormat:@"Photos:%d", [[result objectForKey:@"photos"] count] ]);
//NSLog( [NSString stringWithFormat:@"PhotosArray:%d", [[[result objectForKey:@"photos"] valueForKeyPath:@"photo"] count]]);
// http://www.flickr.com/services/api/misc.urls.html
id obj;
for( NSDictionary *photo in [[result objectForKey:@"photos"] valueForKeyPath:@"photo"] ) {
//NSLog( [NSString stringWithFormat:@"Title:%@", [photo objectForKey:@"title"] ] );
//NSLog( [NSString stringWithFormat:@"URL:%@", [NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_s.jpg", [photo objectForKey:@"farm"] , [photo objectForKey:@"server"] , [photo objectForKey:@"id"] , [photo objectForKey:@"secret"]] ] );
[dataSource addObject:
[[NSDictionary alloc] initWithObjectsAndKeys:
( ( (obj = [photo objectForKey:@"title"]) && [obj length] > 0 ) ? obj : @"Untitled" ) ,
@"title" ,
[NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_s.jpg", [photo objectForKey:@"farm"] , [photo objectForKey:@"server"] , [photo objectForKey:@"id"] , [photo objectForKey:@"secret"]],
@"url" ,
nil
]
];
}
[target release];
[data release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//NSLog( [NSString stringWithFormat:@"Cnt:%d" , [dataSource count]] );
return [dataSource count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
cell.textLabel.text = [[dataSource objectAtIndex:indexPath.row] objectForKey:@"title"];
//NSLog( [NSString stringWithFormat:@"%@", [[dataSource objectAtIndex:indexPath.row] objectForKey:@"title"]] );
cell.imageView.image = [UIImage imageWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
[[dataSource objectAtIndex:indexPath.row] objectForKey:@"url"]
]
]
];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
// AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
// [self.navigationController pushViewController:anotherViewController];
// [anotherViewController release];
}
- (void)dealloc {
[dataSource release];
[super dealloc];
}
@end
在此實作一個 http_build_query 來使用,目前流程上可以取得 Flickr API 回傳的訊息,並且在組出照片出來,但是,我在模擬器上跑的結果還滿慢的,此例利用 "台灣正妹" 找 10 張照片來呈現,若只印照片名稱不慢,但加上讀出縮圖部分,花 5 秒鐘才跑出來,或許還有一些技術可以嘗試看看。
請問一下除了你KEY的程式碼以外還要再加什麼嗎??
回覆刪除因為KEY完程式碼之後我只有出現格子,沒有出現圖片跟台灣正妹的字
謝謝
版主回覆:(08/07/2012 01:32:52 AM)
你可以看看 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 這個 function, 裡頭就是在更新格子裡的圖片跟文字
當然也有可能從 Flickr 取得的資料有問題或是他的結構有更新, 你在把 dataSource 印出來看看比對, 例如查詢完資料後(viewDidLoad裡), 使用 NSLog( @"%@" , dataSource )
剛剛TRY了一下
回覆刪除發現程式根本就不會進入- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 這個 function裡
所以可以請問一下這個function在哪裡呼叫的
版主回覆:(08/31/2010 03:46:36 AM)
這個是程式架構上自動會呼叫的, 不然你可以試試自己建立一個 UITableViewController, 再把一些藍色標記的填補進去試試囉