在这个教程中,将使用iOS 5和Xcode4.2,以及Storyboard来创建一个简单的Twitter应用。这个应用将展示推文列表,并且当点击某个推文时,会显示关于该推文和用户的详细信息。
在Xcode中,首先创建一个新的项目。选择“文件” > “新建” > “新建项目”。然后选择“主-详情应用程序”,点击“下一步”。输入产品名称为'Twitter Test',并确保选中“使用Storyboard”和“使用自动引用计数”复选框。点击“下一步”,选择保存项目的位置,然后点击“创建”。现在项目已经创建好了,它会自动设置一个Storyboard、一个主视图控制器和一个详情视图控制器。Storyboard是设计应用的地方,控制器是编写程序的地方。
打开Storyboard,看看它包含什么。点击左侧的MainStoryboard.storyboard,Storyboard就会出现。从这里可以看到:
控制器之间的线称为'segues'。稍后在处理用户选择推文的动作时会详细讨论这些。
在左侧的树视图中选择表格视图,并在右侧选择“动态原型”。这告诉表格视图将从控制器动态设置单元格。
接下来,在左侧的树视图中选择“表格视图单元格”,然后在右侧将样式设置为“副标题”,并将“标识符”设置为'TweetCell'——这是在代码中用来找到单元格以便填充其详细信息的名称。
现在,已经在Storyboard部分设置了主视图。稍后会回到详情视图。
进入MasterViewController.m,替换@interface到@end部分的代码:
@interface MasterViewController : UITableViewController {
NSArray *tweets;
}
- (void)fetchTweets;
@end
这告诉Objective-C有一个tweets实例变量和一个fetchTweets方法。
现在来实际获取推文。回到MasterViewController.m并插入以下方法:
- (void)fetchTweets {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString:@"https://api.twitter.com/1/statuses/public_timeline.json"]];
NSError* error;
tweets = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
这个方法的作用是在单独的线程中获取JSON数据,然后在主线程中更新表格视图。这样做的原因是,如果在主线程中获取数据,那么应用程序会在数据加载完成之前锁定。
在viewDidLoad方法中调用这个方法:
- (void)viewDidLoad {
[super viewDidLoad];
[self fetchTweets];
}
这告诉应用程序在视图加载时加载JSON数据。现在Twitter feed已经加载到名为tweets的实例变量中。这个变量现在包含一个数组,其中包含多个NSDictionary对象,NSDictionary是一个键/值集合。
接下来,插入以下两个方法:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *text = [tweet objectForKey:@"text"];
NSString *name = [[tweet objectForKey:@"user"] objectForKey:@"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:@"by %@", name];
return cell;
}
现在尝试运行应用程序——点击“运行”图标。它工作了:非常简单,对吧?
尝试选择一个推文。意图是加载详情视图,但这并没有发生。为什么?让回到Storyboard:
注意到主视图控制器和详情视图控制器之间没有segue。当之前从静态单元格切换到原型单元格时,它被擦除了,所以现在它不知道该做什么。让帮助它。
在左侧,使用CTRL + 拖动从“表格视图单元格”到“详情视图控制器”,并选择“Push”。现在它知道该做什么了,所以尝试再次运行。
点击表格视图和详情视图之间的segue,并将其标识符更改为'showTweet'。这就是稍后在代码中识别它的方式。
在MasterViewController.m中,紧接在#import "MasterViewController.h"之后插入以下代码:
#import "DetailViewController.h"
这是为了让能够从主视图控制器访问详情视图控制器。插入以下方法:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showTweet"]) {
NSInteger row = [[self tableView].indexPathForSelectedRow row];
NSDictionary *tweet = [tweets objectAtIndex:row];
DetailViewController *detailController = segue.destinationViewController;
detailController.detailItem = tweet;
}
}
这基本上告诉应用程序在主视图和详情视图之间的segue时该做什么。它识别选定的行,找到推文,然后在详情视图控制器上设置detailItem属性。(detailItem属性是在最初选择“主-详情应用程序”时自动创建的。感谢Xcode!)
打开Storyboard,双击'Master'和'Detail'标题,分别更改为'Tweets'和'Tweet':
删除标签“详情视图内容放在这里”。将创建自己的。插入一个用于名称的标签,一个用于推文的标签,以及一个用于个人资料图片的图像视图,如下所示:
可以按想要的方式自定义文本大小。
需要一种方法从代码中调用这些新控件。这是通过创建三个输出完成的,这是一种连接视图和控制器的线或连接。在DetailViewController.h中,用以下代码替换@interface行:
@interface DetailViewController : UIViewController {
IBOutlet UIImageView *profileImage;
IBOutlet UILabel *nameLabel;
IBOutlet UILabel *tweetLabel;
}
现在已经创建了输出。现在需要引用或连接这些视图。在左侧,使用CTRL + 拖动从详情视图控制器到“标签 - 名称放在这里”并选择nameLabel:
同样从详情视图控制器拖动到“标签 - 推文放在这里”并选择tweetLabel,以及从详情视图控制器拖动到“图像视图”并选择profileImage。
- (void)configureView {
if (self.detailItem) {
NSDictionary *tweet = self.detailItem;
NSString *text = [[tweet objectForKey:@"user"] objectForKey:@"name"];
NSString *name = [tweet objectForKey:@"text"];
tweetLabel.lineBreakMode = UILineBreakModeWordWrap;
tweetLabel.numberOfLines = 0;
nameLabel.text = text;
tweetLabel.text = name;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *imageUrl = [[tweet objectForKey:@"user"] objectForKey:@"profile_image_url"];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
dispatch_async(dispatch_get_main_queue(), ^{
profileImage.image = [UIImage imageWithData:data];
});
});
}
}