20110625
http://www.cnblogs.com/scorpiozj/archive/2011/06/27/2091259.html
做iphone的,UINavigationController+UItableViewController组合的应用是必须会的,那么我们必须学习下如何使用320来实现此类应用。
考虑这样的需求:点击tableview上的feed行就从apple store上获取排名前10的专辑信息,链接:http://itunes.apple.com/us/rss/topalbums/limit=10/explicit=true/xml. 具体流程:程序启动后是一tableview(图1),点击rss 后在tableview中显示所有专辑信息,每个cel(图2)l中左边是专辑图片,右上是歌手名,右下是专辑名.流程很简单,页面显示就两个:通常rooview+detailView就搞定,此外还需要一个获取数据的类就OK了.参考了320的一种现成的cell式样,好像没有和我们所需相同的,那么我们需要自定义cell.
图1
图2
在开始工程前,我们首先看一下320中实现上述的tableview的一个大致框架(图3):
图3
在UIKit下,tableview的实现主要通过设置tableview的datasource和delegate,这是苹果典型的MVC思维。在320中也贯彻了MVC思想:需要tableviewController,dataSource,tableItem,tableItemCell,Model以及object。datasource,顾名思义是为tableview提供数据,它首先生成model来获得所需数据(FeedObj),然后根据原数据的类型生成相应的tableItem,最后根据tableItem来选择合适的tableItemCell。
下面我们开始创建工程,可以选择xcode的viewController模板,然后将320添加进工程(过程略,参见官方的脚本文件).做好以上设置后,就可以开始了.我们大概需要以下类文件:rootViewController,tableViewController,tableItem,tableItemCell,Model,dataSource和obj.
以下将代码列出,并做简单说明.
MyThree20ProjAppDelegate主要的操作是生成url映射.

#import <UIKit/UIKit.h>
@class MyThree20ProjViewController;
@interface MyThree20ProjAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyThree20ProjViewController *viewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet MyThree20ProjViewController *viewController;
@end
#import "MyThree20ProjAppDelegate.h"
#import "MyThree20ProjViewController.h"
#import <Three20/Three20.h>
@implementation MyThree20ProjAppDelegate
@synthesize window;
@synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
TTNavigator *navigator = [TTNavigator navigator];
navigator.persistenceMode = TTNavigatorPersistenceModeAll;
navigator.window = self.window;
TTURLMap *map = navigator.URLMap;
// Any URL that doesn't match will fall back on this one, and open in the web browser
// [map from:@"*" toViewController:[TTWebController class]];
[map from:@"tt://root" toSharedViewController:NSClassFromString(@"MyThree20ProjViewController")];
[map from:@"tt://feed" toSharedViewController:NSClassFromString(@"RssFeedTableViewController")];
if (![navigator restoreViewControllers]) {
[navigator openURLAction:[TTURLAction actionWithURLPath:@"tt://root"]];
}
}
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)URL {
[[TTNavigator navigator] openURLAction:[TTURLAction actionWithURLPath:URL.absoluteString]];
return YES;
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
接着就是熟悉的RootViewController,这里生成一个tableview

#import <UIKit/UIKit.h>
@interface MyThree20ProjViewController : TTTableViewController {
}
@end
#import "MyThree20ProjViewController.h"
@implementation MyThree20ProjViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"My Three20";
self.dataSource = [TTSectionedDataSource dataSourceWithObjects:
@"F",
[TTTableTextItem itemWithText:@"feed" URL:@"tt://feed"],
nil];
}
- (void)dealloc {
[super dealloc];
}
@end
RssFeedTableViewController,是320中tableViewController的子类,可以通过不同的datasource显示不同的view.

#import <UIKit/UIKit.h>
@interface RssFeedTableViewController : TTTableViewController {
}
@end
#import "RssFeedTableViewController.h"
#import "RssFeedDataSource.h"
@implementation RssFeedTableViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
self.title = @"Rss";
self.variableHeightRows = YES;//很重要,否则cell高度无法改变
}
return self;
}
- (void)createModel {
self.dataSource = [[[RssFeedDataSource alloc] init] autorelease];
}
- (id<UITableViewDelegate>)createDelegate {
return [[[TTTableViewDragRefreshDelegate alloc] initWithController:self] autorelease];//下拉刷新,但本文未实现
}
- (void)dealloc {
[super dealloc];
}
@end
我们接着看下RssFeedDataSource,因为我们的tableview不需要section,所以是listdatasource的子类.通过model来获取所需数据,并生成相应的tableItem对象集合

#import <Foundation/Foundation.h>
#import "RssFeedModel.h"
@interface RssFeedDataSource : TTListDataSource {
RssFeedModel *_feedModel;
}
-(id)initWithQuery:(NSString *)query;
@end
#import "RssFeedDataSource.h"
#import "RssFeedObject.h"
#import "RssTableItem.h"
#import "RssTableItemCell.h"
@implementation RssFeedDataSource
-(id) init
{
if (self = [super init])
{
_feedModel = [[RssFeedModel alloc] initWithSearchQuery:@"a"];
}
return self;
}
-(id)initWithQuery:(NSString *)query
{
if (self = [super init])
{
_feedModel = [[RssFeedModel alloc] initWithSearchQuery:query];
}
return self;
}
- (void)dealloc
{
TT_RELEASE_SAFELY(_feedModel);
[super dealloc];
}
- (id<TTModel>)model {
return _feedModel;
}
- (void)tableViewDidLoadModel:(UITableView*)tableView
{
NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:1];
for (RssFeedObject *rssObj in _feedModel.dataArr)
{
RssTableItem *itemObj = [RssTableItem itemWithRssFeedObj:rssObj];
[items addObject:itemObj];
}
self.items = items;
TT_RELEASE_SAFELY(items);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (NSString*)titleForLoading:(BOOL)reloading {
if (reloading) {
return NSLocalizedString(@"Updating Twitter feed...", @"Twitter feed updating text");
} else {
return NSLocalizedString(@"Loading Twitter feed...", @"Twitter feed loading text");
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (NSString*)titleForEmpty {
return NSLocalizedString(@"No tweets found.", @"Twitter feed no results");
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (NSString*)subtitleForError:(NSError*)error {
return NSLocalizedString(@"Sorry, there was an error loading the Twitter stream.", @"");
}
- (Class)tableView:(UITableView*)tableView cellClassForObject:(id) object {
if ([object isKindOfClass:[RssTableItem class]]) {
return [RssTableItemCell class];
} else {
return [super tableView:tableView cellClassForObject:object];
}
}
- (void)tableView:(UITableView*)tableView prepareCell:(UITableViewCell*)cell
forRowAtIndexPath:(NSIndexPath*)indexPath {
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
@end
我们再看下model是怎么工作的.因为我们需要请求数据,所以生成TTURLRequestModel的子类,在requestDidFinishLoad:方法解析数据,获得的结果会返回给datasource

#import <Foundation/Foundation.h>
@interface RssFeedModel : TTURLRequestModel {
NSString *_queryStr;
NSMutableArray *_dataArr;
}
@property (nonatomic, copy) NSString *queryStr;
@property (nonatomic, retain) NSMutableArray *dataArr;
- (id)initWithSearchQuery:(NSString*)searchQuery;
@end
#import "RssFeedModel.h"
#import "RssFeedObject.h"
#define kID @"id"
#define kImArtist @"im:artist"
#define kLink @"link"
#define kCategory @"category"
#define kTitle @"title"
#define kImContentType @"im:contentType"
#define kRights @"rights"
#define kImPrice @"im:price"
#define kImImage @"im:image"
#define kImReleaseDate @"im:releaseDate"
#define kImItemCount @"im:itemCount"
#define kImName @"im:name"
#define kUpdated @"updated"
#define kContent @"content"
#define KeysArray nil
#define KEntityName @"___Entity_Name___"
#define kEntityType @"___Entity_Type___"
#define kEntityValue @"___Entity_Value___"
#define kHeight @"height"
#define kImageHeight @"60"
#define kRSSFeed @"http://itunes.apple.com/us/rss/topalbums/limit=10/explicit=true/xml"
@implementation RssFeedModel
@synthesize queryStr = _queryStr, dataArr = _dataArr;
- (id)initWithSearchQuery:(NSString*)searchQuery
{
if (self = [super init])
{
self.queryStr = searchQuery?searchQuery:@"";
_dataArr = [[NSMutableArray alloc] initWithCapacity:1];
}
return self;
}
- (void) dealloc {
TT_RELEASE_SAFELY(_queryStr);
TT_RELEASE_SAFELY(_dataArr);
[super dealloc];
}
- (void)load:(TTURLRequestCachePolicy)cachePolicy more:(BOOL)more
{
NSString* url = [NSString stringWithFormat:@"%@",kRSSFeed];
TTURLRequest* request = [TTURLRequest
requestWithURL: url
delegate: self];
request.cachePolicy = cachePolicy;
request.cacheExpirationAge = TT_CACHE_EXPIRATION_AGE_NEVER;
TTURLXMLResponse* response = [[TTURLXMLResponse alloc] init];
response.isRssFeed = YES;
request.response = response;
TT_RELEASE_SAFELY(response);
[request send];
}
- (void)requestDidFinishLoad:(TTURLRequest*)request
{
// [NSArray arrayWithObjects:kID,kImArtist,kLink,kCategory,kTitle,kImContentType
TTURLXMLResponse *xmlResponse = request.response;
NSDictionary *feed = xmlResponse.rootObject;
NSArray *allKey = [feed allKeys];
NSMutableArray *mArr = [NSMutableArray arrayWithCapacity:1];
for (NSString *str in allKey)
{
NSLog(@"%@\n",str);
[mArr addObject:[feed objectForKey:str]];
}
NSArray *entry = [feed objectForKey:@"entry"];//rss feed:每个是一个dictionary代表一行
for(NSDictionary *item in entry)
{
RssFeedObject *obj = [[RssFeedObject alloc] init];
for( NSString *str in [item allKeys])
{
id attr = [item valueForKey:str];
if ([attr isKindOfClass:[NSDictionary class]])
{
NSArray *attrKeys = [attr allKeys];
NSString *entityName = [(NSDictionary *)attr valueForKey:KEntityName];
if ([entityName isEqualToString:kID])
{
obj.readMore = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kImArtist])
{
obj.artist = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kCategory])
{
obj.genre = [(NSDictionary *)attr valueForKey:@"label"];
}
else if ([entityName isEqualToString:kTitle])
{
obj.header = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kRights])
{
obj.copyRight = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kImPrice])
{
obj.price = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kImReleaseDate])
{
obj.releaseDate = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kImItemCount])
{
obj.itemCount = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kImName])
{
obj.title = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else if ([entityName isEqualToString:kUpdated])
{
obj.updateDate = [(NSDictionary *)attr valueForKey:kEntityValue];
}
else {
}
}
else if ([attr isKindOfClass:[NSArray class]])
{
for (NSDictionary *imagDic in attr)
{
NSString *height = [imagDic valueForKey:kHeight];
if ([height isEqualToString:kImageHeight])
{
obj.imageLink = [imagDic valueForKey:kEntityValue];
}
}
}
}
[self.dataArr addObject:obj];
TT_RELEASE_SAFELY(obj);
}
NSDictionary *item = [entry objectAtIndex:0];
NSArray *itemKeys = [item allKeys];
NSMutableArray *itemContent = [NSMutableArray arrayWithCapacity:1];
for(NSString *str in itemKeys)
{
[itemContent addObject:[item objectForKey:str]];
NSLog(@"%@:%@\n",str,[item objectForKey:str]);
}
NSLog(@"%@",feed);
[super requestDidFinishLoad:request];//important
}
@end
tableItemCell就是自定义的cell,tableItem是tableviewcell所代表的object,在cell中需要将object与view(就是cell)联系起来.在这里我们也可以体会到MVC在iOS中真的是无处不在.

#import <Foundation/Foundation.h>
@class RssFeedObject;
@interface RssTableItem : TTTableItem
{
NSString *_artist;
NSString *_title;
NSString *_link;
NSString *_imageName;
}
@property (nonatomic, copy) NSString *artist;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *link;
@property (nonatomic, copy) NSString *imageName;
+(id)itemWithRssFeedObj:(RssFeedObject *)obj;
-(id)initWithRssFeedObj:(RssFeedObject *)obj;
@end
#import "RssTableItem.h"
#import "RssFeedObject.h"
@implementation RssTableItem
@synthesize artist = _artist, title = _title,link = _link,imageName = _imageName;
- (id)init
{
if (self = [super init])
{
_artist = nil;
_title = nil;
_link = nil;
_imageName = nil;
}
return self;
}
+(id)itemWithRssFeedObj:(RssFeedObject *)obj
{
RssTableItem *item = [[[RssTableItem alloc] init] autorelease];
item.artist = obj.artist;
item.title = obj.title;
item.link = obj.readMore;
item.imageName = obj.imageLink;
return item;
}
-(id)initWithRssFeedObj:(RssFeedObject *)obj
{
if (self = [[RssTableItem alloc] init])
{
self.artist = obj.artist;
self.title = obj.title;
self.link = obj.readMore;
self.imageName = obj.imageLink;
}
return self;
}
- (void)dealloc
{
TT_RELEASE_SAFELY(_artist);
TT_RELEASE_SAFELY(_title);
TT_RELEASE_SAFELY(_title);
TT_RELEASE_SAFELY(_imageName);
[super dealloc];
}
@end

#import <Foundation/Foundation.h>
@interface RssTableItemCell : TTTableViewCell {
UILabel *_artistLab;
UILabel *_titleLab;
TTImageView *_myImageView;
}
@end
#import "RssTableItemCell.h"
#import "RssTableItem.h"
@implementation RssTableItemCell
+ (CGFloat)tableView:(UITableView*)tableView rowHeightForObject:(id)object
{
return 60;
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier
{
if (self = [super initWithStyle:style reuseIdentifier:identifier])
{
_myImageView = [[TTImageView alloc] initWithFrame:CGRectMake(5, 0, 60, 60)];
[self.contentView addSubview:_myImageView];
_artistLab = [[UILabel alloc] initWithFrame:CGRectMake(70, 10, 200, 10)];
_artistLab.textColor = [UIColor grayColor];
_artistLab.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:_artistLab];
_titleLab = [[UILabel alloc] initWithFrame:CGRectMake(70, 30, 200, 20)];
_titleLab.textColor = [UIColor grayColor];
_titleLab.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:_titleLab];
}
return self;
}
- (void)dealloc
{
TT_RELEASE_SAFELY(_artistLab);
TT_RELEASE_SAFELY(_myImageView);
TT_RELEASE_SAFELY(_titleLab);
[super dealloc];
}
- (void)layoutSubviews
{
[super layoutSubviews];
}
- (id)object
{
return self;;
}
- (void)setObject:(id)obj
{
if (self != obj)
{
[super setObject:obj];
RssTableItem *item = obj;
_myImageView.urlPath = item.imageName;
_artistLab.text = item.artist;
_titleLab.text = item.title;
}
}
@end
最后是一个解析数据的对象模型RssFeedObject.

#import <Foundation/Foundation.h>
@interface RssFeedObject : NSObject {
NSString *_header;
NSString *_title;
NSString *_artist;
NSString *_genre;
NSString *_price;
NSString *_releaseDate;
NSString *_copyRight;
NSString *_readMore;
NSString *_itemCount;
NSString *_imageLink;
NSString *_updateDate;
}
@property (nonatomic, copy) NSString *header,*title,*artist,*genre;
@property (nonatomic, copy) NSString *price,*releaseDate,*copyRight,*readMore;
@property (nonatomic, copy) NSString *itemCount,*imageLink,*updateDate;
@end
#import "RssFeedObject.h"
@implementation RssFeedObject
@synthesize header=_header,title=_title,artist=_artist,genre=_genre;
@synthesize price=_price,releaseDate=_releaseDate,copyRight=_copyRight,readMore=_readMore;
@synthesize itemCount = _itemCount,imageLink = _imageLink,updateDate = _updateDate;
- (void)dealloc
{
TT_RELEASE_SAFELY(_header);
TT_RELEASE_SAFELY(_title);
TT_RELEASE_SAFELY(_artist);
TT_RELEASE_SAFELY(_genre);
TT_RELEASE_SAFELY(_price);
TT_RELEASE_SAFELY(_releaseDate);
TT_RELEASE_SAFELY(_copyRight);
TT_RELEASE_SAFELY(_readMore);
TT_RELEASE_SAFELY(_itemCount);
TT_RELEASE_SAFELY(_imageLink);
TT_RELEASE_SAFELY(_updateDate);
[super dealloc];
}
@end
至此我们的工程就完成了.程序的大致流程如图3所示,具体的代码细节都是很容易理解的.如果有写的不明白或者错误的地方请多多指正.
谢谢.
ps:有谁知道mac下有没有啥好的博客客户端啊,不然家里都打不开,只能到公司里抽时间写