ios 开发入门二

欢迎来到iPhone初学者示范程序教程的第二部分(总共三部分)。这个程序的功能是给恐怖昆虫打分!

在这套教程的 第一个部分, 我们的程序已经有一个table view,并能将一些昆虫在table view中列出来。

在第二部分,我们会学习如何创建一个详细视图(detail view)。在详细视图中, 用户可以看到一个更大的昆虫图片,给昆虫打分,并可以随意更换这些图片!

在这套教程的第三部分 ,
我们将学习如何添加新的昆虫。我们还会了解如何给程序加上图标和启动程序时显示的图片,如何处理耗费内存和时间的运算。

那我们就开始吧!

你好, 视图控制器!

我们已经有一个昆虫列表了。我们希望能点击一个昆虫来进入一个界面。然后在这个界面修改昆虫的名字,图片,和给昆虫打分。

iPhone程序中,每个屏幕界面,往往会有一个相对应的视图控制器类。现在我们程序一启动就有一个主视图控制器(MasterViewController)来控制table view。当你点击一个昆虫,我们需要有一个相应的详细视图控制器(DetailViewController)来显示这个昆虫的一些信息。

我们程序的默认模版本来已经有我们需要的功能了。但是我们对table view cells的改动让这个功能丧失了。不过我们很快会解决这个问题的。

一个视图控制器可以控制许多的视图。我们的table view控制器只有一个视图–那个table view。 但是详细视图控制器却有好几个视图 – 我们需要一个视图来显示昆虫的名字,一个视图显示昆虫图片,一个视图显示评分等等。

下载些东西!

在详细视图里,我们需要一个5星的评分视图, 但是iPhone里并没有现成可用的。幸好,我最近还编写了另外一片教程, 是关于如何在IOS5上创建自定义UIView: 一个5星评分视图, 所以我们将直接使用那个教程的成果。

你现在不需要去读那个教程(除非你想去读) – 你可以直接在 这里下载和现在这个教程有关的代码。

先下载那个文件,然后:

  • 在XCode中建立一个新的文件组,叫做”Views“, 并将RateView.h/RateView.m拖到这个文件组中。 请确保给”Copy items into destination group’s folder (if needed)”选项打钩。这就是另外一个教程中的5星排名视图的代码。
  • 重复上面的步骤, 将UIImageExtras拖入一个叫”Helpers“的新建的文件组中. 我们等下会用这里面的代码来重新设置图片的大小.
  • 重复上述步骤, 将那三个惊恐表情的图片拖入一个新的叫“Art”的文件组里. 我们用这些图片代替5星来增加些喜剧效果。

    重复上述步骤,将logo1.png拖到Art文件组里。我们等下会把它设为我们程序的图标。

用故事版编辑器来设计我们的详细视图控制器

Ok-我们终于准备好了!打开MainStoryboard.storyboard,如果你看最右边,应该能看到一个模版自动生成的详细视图控制器。它有 “Detail view content goes here” 的标签:

The detail view controller in the storyboard editor

故事版编辑器帮助你直观的设计用户界面。你把用户界面元素直接拖放到你的视图里,在属性栏里填入各个属性的值,你甚至可以直接将用户界面元素和视图控制器类的属性一一对应链接起来。

理解这些的最快方法是自己动手试试!首先,点击那个视图控制器,在屏幕上方菜单栏里选择EditorCanvasShow Bounds Rectangles – 这样我们就能清楚看到用户界面元素的边界,方便我们部署各个元素。

将写着 “Detail view content goes here”的标签删掉 – 我们不会再需要它了!

然后在右边工具栏的下半部分,点击选择第三个标签 – Object Library (对象库)。分别将一个UITextField,一个UIImageView和一个UIView拖入故事版中,并将它们如下图一样部署(最顶端的是UITextField)。

Laying out the interface in the storyboard editor

点击选择那个UITextField。然后在右边工具栏的上半部分,选择第四个标签,叫Attributes Inspector(属性设置)。我们需要修改一些属性。

像下图一样,将font(字体)设为CustomHelveticaBoldSize 18.0,将text alignment(文字对齐)设为center(居中对齐),将clear button behavior 设为“Appears While Editing”,将capitalization(大小写格式)设为”Words“:

Setting text field attributes

然后,点击选择第五个标签,切换到面积设置栏(Size Inspector),将autosizing的属性修改如下图:

Autosizing Attributes

设置这个属性的作用是,当我们将手机旋转到横向时,那个文本输入框的宽度会随之增加,保持它所占屏幕的横向比例。

下一步是设置UIImageView。点击选择UIImageView,在右边工具栏的属性设置栏(Attributes Inspector),将mode(填充模式)设为”Aspect Fit”。在工具栏的第三栏 – 面积设置栏(Size Inspector),将autosizing属性修改如下图:

Autosizing Attributes

这样,图片视图就会根据现在屏幕的方向而变大缩小,以保持它的边界离屏幕各个边界的距离。同时,图片也会在保持自身比例的前提下改变大小,以占据所有空间。

最后,点击选择UIView,选择第三栏 – 身份设置栏(Identity Inspector)然后将Class Identity设为”RateView“,这样我们的5星评分视图就会在这里出现。在第五栏 – 面积设置栏将autosizing属性修改如下图:

Autosizing Attributes

这会使它左右拉长,但保持高度不变。

下面,我们将给这个屏幕界面增加些控制组件,让用户能通过点击UIImageView来改变昆虫图片。.

可用的方法有好几种,但最简单的是在UIImageView上放一个隐形的按钮,当用户点击按钮时,回呼一个函数。我们还将在UIImageView下面加一个UILable(标签)。标签上写着”Tap to Change Picture”(点击改换图片)。这样如果还没有图片, 用户就会看到这个标签。

从右下角的库里拖出一个Round Rect Button(椭圆形按钮),并调整大小,使它和UIImageView一样大。在工具栏第四栏 – 属性设置栏将它的type属性设为Custom。然后在第三栏 – 面积设置栏,将autosizing属性修改如下图:

Autosizing Attributes

最后,从库里拖出一个UILabel(标签),将它放在UIImageView的正中间,双击那个UILabel,将它的内容改为”Tap To Change Image”(点击改变图片)。将text alignment(文字对齐)属性设为center(居中对齐)。并且,在左边的控制栏里(如下图),将UILabel向上拖几个,使其出现在UIImageView的下方(那个列表越靠上的组件越早被生成,所以越靠上组件就出现在底部,靠下的组件会覆盖靠上的组件):

Setting the label to show up in the back

然后在第五栏 – 面积设置栏,将autosizing的属性修改如下图:

Autosizing Attributes

现在是时候确认一下我们是不是正确的设置了所有autosizing属性。点击选择详细视图控制器,然后将手机的Orientation属性从Portrait(纵向)改为Landscape(横向):

Testing autosizing attributes and orientation changes work OK in Storyboard editor

如果看起来不对,不用担心 – 将方向换回到纵向然后重新检查一下刚才提到的autosizing的属性。

好了!我们已经将所有控制元素放到屏幕上了,现在只需要将它们和outlets一一对应起来就行了。

首先开启助理编辑器(Assistant Editor)。助理编辑器是屏幕上方工具栏中,“Editor“(编辑器)栏的第二个标签。请确保助理编辑器的设置是AutomaticDetailViewController.h:
Bringing up the assistant editor

按住control键,并在那个文本输入栏(UITextField)上按住鼠标左键,并将鼠标拖到DetailViewController.h文件中@end上方的位置。你可以在出现的一个弹出窗口中将那个文本输入栏和DetailViewController类中的一个属性对应起来。给这个属性取名为“titleField”,点击Connect按钮。

Connecting the title field to an outlet

重复上述步骤,将那个图片视图(image view)链接到代码中并命名为”imageView”,将评分视图(Rate View)链接到代码中并命名为”rateView”。

当用户点击那个隐形按钮,我们需要调用一个特定的函数。要达到这个效果,操作步骤与上述步骤相似,只是现在你应该点击那个按钮,将鼠标拖至@end的上一行,将connection type(链接类型)设为“Action”,命名为“addPictureTapped”,然后点击Connect。

Connecting a button to a method in the Storyboard editor

注意,Event属性已经默认为”Touch Up Inside”。这意味着当用户松开那个按钮时,相应的函数就会被调用。

你也可以把其他用户操作和回呼函数对应起来。比如当用户改动文本输入栏的内容时,我们需要调用另一个函数。

按住control键,鼠标左键点住那个文本输入栏,拖动鼠标到@end上方,在弹出窗口中将Connection属性设为“Action”。默认的Event属性是“Editing Did End” - 把它改为“Editing Changed”。在Name属性输入“titleFieldTextChanged” - 这是回呼函数的名字。点击Connect。

最后一件事是把这个类设为文本输入栏的delegate。有的时候,光是在用户操作时调用回呼函数是不够的 - 可能我们会需要更多的信息,这里的文本输入栏就是个很好的例子。

按住control键, 鼠标左键点击屏幕左边界面元素列表栏内的Text Field标签,点住”delegate“旁边的小圈,拖动鼠标使其与Detail View Controller标签连线。

Setting the delegate of a text field

这个时候,你的DetailViewController.h文件应该如下图:

#import <UIKit/UIKit.h>
 
@interface DetailViewController : UIViewController
 
@property (strong, nonatomic) id detailItem;
 
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UITextField *titleField;
@property (weak, nonatomic) IBOutlet RateView *rateView;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
- (IBAction)addPictureTapped:(id)sender;
- (IBAction)titleFieldTextChanged:(id)sender;
 
@end

你应该也注意到了一些奇怪的类别 - IBOutlet和IBAction。故事版编辑器需要这些特殊词来建立界面元素和该类属性之间的联系。简单的说,如果我们在一个属性、函数旁加上IBOutlet或IBAction,Interface Builder就能发现它们,允许我们用上述方式与界面元素建立一一对应关系。

在故事版编辑器中完成这一系列设置后,属性和控制元素已经一一对应了。你也可以按住control键然后点击一个Detail View Controller 来查看这些对应关系。我们把这种对应关系叫做“outlets”.

我们还需要做一些小的改动,比如标识视图控制器,使其实现一些delegates。 我们还需要加上一个image picker(图片选择器)的属性。 还有就是修改detailItem,使其成为ScaryBugDoc类。所有改变如下:

#import <UIKit/UIKit.h>
#import "RateView.h"
 
@class ScaryBugDoc;
 
@interface DetailViewController : UIViewController <UITextFieldDelegate, RateViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>
 
@property (strong, nonatomic) ScaryBugDoc * detailItem;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UITextField *titleField;
@property (weak, nonatomic) IBOutlet RateView *rateView;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (strong, nonatomic) UIImagePickerController * picker;
 
- (IBAction)addPictureTapped:(id)sender;
- (IBAction)titleFieldTextChanged:(id)sender;
 
@end

头文件和其他准备工作就绪,只剩实现这些类了。

实现详细视图

我们将对DetailViewController.m做一系列的改动。让我们一步步来解释这些改动。

1) 导入头文件

// 加在文件顶端
#import "ScaryBugDoc.h"
#import "ScaryBugData.h"
#import "UIImageExtras.h"
 
// 加在synthesize部分
@synthesize picker = _picker;

2)设置评分视图

// 将configureView函数换成如下
- (void)configureView
{
    self.rateView.notSelectedImage = [UIImage imageNamed:@"shockedface2_empty.png"];
    self.rateView.halfSelectedImage = [UIImage imageNamed:@"shockedface2_half.png"];
    self.rateView.fullSelectedImage = [UIImage imageNamed:@"shockedface2_full.png"];
    self.rateView.editable = YES;
    self.rateView.maxRating = 5;
    self.rateView.delegate = self; 
}

configureView函数会在viewDidLoad函数中被调用。 在configureView函数中我们设置RateView的各个属性。如果你想了解更多RateView的属性设置,可以读读 如何在IOS5上创建自定义UIView: 一个5星评分视图 教程.

3)屏幕自动重新布局

// 将shouldAutorotateToInterfaceOrientation函数的返回值换为如下:
return YES;

在shouldAutorotateToInterfaceOrientation函数中,我们的返回值永远是YES。这是因为我们在Interface Builder中已经将所有的autosizing属性设置好了!用户可以随意转动手机,屏幕会根据autosizing属性重新布局。

4) 设置起始的用户界面状态

// 加在configureView的末端
if (self.detailItem) {
    self.titleField.text = self.detailItem.data.title;
    self.rateView.rating = self.detailItem.data.rating;    
    self.imageView.image = self.detailItem.fullImage;
}

这里我们只是根据选中的昆虫信息来设置用户界面信息。

5) 处理文本视图和评分视图

- (IBAction)titleFieldTextChanged:(id)sender {
    self.detailItem.data.title = self.titleField.text;
}
 
#pragma mark UITextFieldDelegate
 
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    return YES;
}
 
#pragma mark RateViewDelegate
 
- (void)rateView:(RateView *)rateView ratingDidChange:(float)rating {
    self.detailItem.data.rating = rating;
}

只要用户改变文本输入栏的值,我们就会调用titleFieldValueChanged函数,这样数据模型就会被更新。

当用户点击回车键时,textFieldShouldReturn函数就会被调用。我们在这里调用resignFirstResponder函数来隐藏键盘。

因为我们已经将现在这个类的实例设置为RateView的delegate,所以当用户选择打分时,rateView:ratingIsChanged函数就会被调用,同时在这里我们更新数据模型。

那个“#pragma marks”是XCode的特殊字符,以便XCode在函数列表中加入分割线,方便开发者分类一个文件中的函数。

Using pragma mark to organize code in Xcode

6) 显示图片选择器并处理图片

- (IBAction)addPictureTapped:(id)sender {
    if (self.picker == nil) {   
        self.picker = [[UIImagePickerController alloc] init];
        self.picker.delegate = self;
        self.picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        self.picker.allowsEditing = NO;    
    } 
    [self.navigationController presentModalViewController:_picker animated:YES];    
}
 
#pragma mark UIImagePickerControllerDelegate
 
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [self dismissModalViewControllerAnimated:YES];
}
 
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {    
 
    [self dismissModalViewControllerAnimated:YES];
 
    UIImage *fullImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage]; 
    UIImage *thumbImage = [fullImage imageByScalingAndCroppingForSize:CGSizeMake(44, 44)];
    self.detailItem.fullImage = fullImage;
    self.detailItem.thumbImage = thumbImage;
    self.imageView.image = fullImage;
}

用户只要点击UIImage上的那个隐形的按钮,我们就会调用addPictureTapped函数。在addPictureTapped函数里,我们实例化一个UIImagePicker(如果它还不存在),然后把图片来源设为照片库(你也可以选择其他来源,比如照相机)。我们把self设为delegate,这样当用户完成选择图片的操作后,self可以接受回叫函数。最后,图片选择器会以全屏(modal)的形式出现。

用户完成图片选择,或者取消图片选择时,我们调用相应的回叫函数。所以,我们还需要实现这些回叫函数。不管怎么样,我们都需要消除那个全屏的视图控制器。如果用户选择了一个图片,我们获取那个图片和它的图片略图(我们可以用刚才载入的UIImageExtras 类来缩小图片),然后更新数据模型和视图。

已经写了很长的代码了。别急,马上就完成了!

整合详细视图

这应该能很快完成。首先,打开MainStoryboard.storyboard,点击选择主视图控制器中的table view cell。按住Control键,点住那个cell然后拖至详细视图控制器。你会看到一个弹出窗口,问你想用哪一种模式链接。选择Push,你会看到那两个视图控制器之间出现一个箭头。

Connecting the view controllers with a push segue

当用户点击某一行时,我们需要将那个昆虫的信息传给详细视图控制器。打开MasterViewController.m并做如下改动:

// 加在文件顶端
#import "DetailViewController.h"
 
// 加在viewWillAppear函数的末端。
[self.tableView reloadData];
 
// 加在文件的末端。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    DetailViewController *detailController =segue.destinationViewController;
    ScaryBugDoc *bug = [self.bugs objectAtIndex:self.tableView.indexPathForSelectedRow.row];
    detailController.detailItem = bug;
}

首先,请注意到我们在viewWillAppear函数里,重新把数据载入那个table中。这样做的原因是用户可能已经在详细视图中改变了虫子的名字或图片,然后在回到table view时,我们需要将这些改变更新到table中。重载整个table是最简单的方法。

然后,记得我们在故事版编辑器曾经做了设置,只要用户点击点击任何一行,程序会将一个详细视图控制器推到栈堆上。这个时候prepareForSegue函数就会被调用,在这个函数里我们可以将需要的信息传递给详细视图控制器。所以我们把需要显示的昆虫的信息传过去了。

终于大功告成了!编译并运行程序,如果一切正常的话,你应该可以打开昆虫的详细视图,随意改变昆虫的名字和图片,给昆虫打分。还可以把屏幕转换到任何朝向。

Detail View Controller Example


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值