【iOS开发教程】iOS 5 APP开发系列教程(第一部分)

对于独立开发者来说, iOS是个非常棒的平台。你能独立地完成一个独特的程序, 然后卖给千万iPhone/iPad用户。我们前面刚刚更新完《Unity3D For iPhone游戏引擎系列教程
最近, 我们收到了很多刚接触iOS开发的朋友的问题。 所以我写了这一系列专门针对初学者的教程。
我们将从零开始, 完成一个完整的程序。 你将会接触到iOS开发的多个方面。为以后深入探索打好基础。
那我们到底要做什么程序呢?

此教程要开发的APP情景是这样的:

有个晚上,我第一次看到了马铃薯虫 的照片。觉得它特别丑,特别吓人!然后我就染上了在网上浏览各种昆虫照片的习惯。 所以我们做的程序就是给这些昆虫打分排名!

在做这个程序的过程中, 我们将触及在iPhone开发中特别常见的部分:

  • iPhone 开发如何入门。
  • 如何用模型来储存数据。
  • 如何使用 Table Views — 插入和删除行。
  • 如何创建详细视图(detail view)。
  • 如何支持横向和纵向显示模式。
  • 如何使用导航控制器(Navigation Controllers)。
  • 如何使用图像选择器(Image Picker)。
  • 如何使用常见的,比如像文字栏, 按钮, 图片视图这样的组件。
  • 如何加入图标和默认图片。
  • 附加: 如何处理耗时运算

听起来好复杂啊。不用担心,我们都是不怕bugs的。
在这个教程的第一部分(总共3部分), 我们将学习如何用数据模型加载一长串虫子的名字,然后将他们在Table View 里列出来。
这个教程是给iOS 初学者的。 但是我们假定你已经对面向对象的C语言和编程有所了解。如果你还不会面向对象的C语言,我建议你先阅读苹果的Objective-C Programming Language Guide。(可惜那个是全英文的。)
首先, 你需要一台苹果电脑。 任何型号的基本上都可以。 只要上面有最新的Mac操作系统 Lion。不过相对便宜的Mac Mini也是可以用来做开发的。
然后你需要下载安装XCode, IOS开发的必备软件。 如果你还没有, 请在苹果开发中心注册一个免费的开发者账户, 并且在官方下载XCode。
你也可以申请注册一个付费的iPhone开发者账号(iPhone Developer Program)。 有了这个账号, 你就可以在苹果应用商店发布你的APP了。不过如果只是为了学习开发, 免费的账号就足够了。
如果你有更高的追求, 那就应该弄来真的苹果产品(iPod, iPhone 4,iPod Touch,iPad)。在虚拟器中做测试是可以的, 但是有的API虚拟器是测试不了的, 一定需要真的苹果产品。

准备工作

Hello, Table View!

我们首先学习使用苹果程序中最常见的组件 —— Table View。你肯定在很多程序中都看到过Table View了。 下面图片是几个例子:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
我们的小程序的第一个界面就是要用Table View 来显示一个昆虫的清单!
打开Xcode, 在启动画面上点Create a new XCode project:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
然后选择Master-Detail Application。 点击Next:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
在下一页, 在Product Name(产品名字) 一栏输入 “ScaryBugs”。 在Company Identifier(公司代码)中输入一串字符。 任意字符也可以, 但是遵从这个格式(com.yourcompanyname 或者 com.yourname)比较好。在Device Family中选择iPhone。在Use Storyboard边上打钩。 在Use Automatic Reference Counting边上打钩。 点击Next:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
选择一个合适的路径来储存你的程序。程序的基本设置就好了。 在顶端的工具栏中选择iPhone Simulator(虚拟器), 然后点击Run:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
现在,你应该在虚拟器中可以看到如下截图:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
你可以点击‘+’按钮来创建新的一行, 然后点击这一行来到一个详细视图(detail view):
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
如你所见,当我们选择Master-Detail Application 的模版时,这个程序就自动生成了。
这个教程将不会具体介绍模版的应用。
我们现在有了一个几乎空白的Table View 和 它的详细视图(Detail View)。 我们需要填入更多数据。我们需要建立一个新的类,作为数据模型来储存我们的虫子的数据。

恐怖的数据模型: 结构

不知你有没有注意到XCode中文件目录栏的层次结构:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
这个程序模版默认有一个根文件组,以及一个Supporting Files(辅助文件)组。 这些组别只是为了方便分类,所以你可以随意改换它们。我们的这个程序将会有很多个文件,所以我们要在分类的时候要考虑到。
首先,建立一个新的组别来归类所有用户界面的文件。用鼠标右键点击Scary Bugs根文件组,然后在菜单中选择New Group(新组别)。然后右键点击新建的组别,将它重命名为“GUI”。把在根文件组的文件都拖到GUI组别下面(不包括Supporting Files)。如下图:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
在我们开始写代码前,先说说我们将如何设计数据模型:

  1. ScaryBugData: 包含昆虫的名字和排分。
  2. ScaryBugDoc: 包括原尺寸的照片,小图片和ScaryBugData数据模型。

我们这样设计数据模型的原因是考虑到这个教程其他部分的需求。在下一部分的教程,我们将学习如何把数据存到硬盘上,如何共享文件,等等。

恐怖的数据模型: 实现

好了,让我们开始吧!右键点击“Model”组别。在菜单中选择“New File”(创建新文件)。在弹出窗口中选择 ”iOSCocoa TouchObjective-C class template“, 然后点击Next。
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
把文件命名为 ScaryBugData。 把NSObject 设为它的父类,点击Next。
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
在弹出窗口最后一页, 点击 “Create”. 如果一切顺利,你的文件目录栏应该如下图一样:
【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
好了,是时候创建ScaryBugData类了。将头文件ScaryBugData.h做如下修改:
#import
@interface ScaryBugData : NSObject
@property (strong) NSString *title;
@property (assign) float rating;
- (id)initWithTitle:(NSString*)title rating:(float)rating;
@end

这些都很简单。我们声明了一个类,这个类有两个属性,一个字符串作为昆虫的名字,还有一个float作为昆虫的恐怖值。我们用两个@property的标签来定义它们:

  • strong:
    这要求运行时自动地保留对这个对象的引用。换而言之,ARC(Automatic Reference Counting)在运行时会一直把这个对象保留在内存里,直到它不再被任何其他对象引用。之后,其所占的内存会被自动释放。
  • assign:
    表示这个属性的值会被直接设定,不需要任何内存管理。一般只用在非对象类的数据,比如 float。
  • 我们还给这个类写了个初始化函数(initializer),这样我们就可以在创建实例时,方便地设定虫子的名字和恐怖值。
    打开ScaryBugData.m文件,把里面的代码替换为下面的代码:
    #import "ScaryBugData.h"
    @implementation ScaryBugData
    @synthesize title = _title;
    @synthesize rating = _rating;
    - (id)initWithTitle:(NSString*)title rating:(float)rating {
    if ((self = [super init])) {
    self.title = title;
    self.rating = rating;
    }
    return self;
    }
    @end

    这也很简单,我们把所有属性综合在一起,用一个初始函数把传入的参数自动填入我们需要的实例变量中。并且这个方法在用完之后不需要释放内存,因为ARC已经帮我们完成了这部分工作。
    好的,ScaryBugData这个类就完成了。现在用同样步骤创建另外一个NSOBject的子类,叫做ScaryBugDoc。
    把 ScaryBugDoc.h 的代码换成下面的:
    #import
    @class ScaryBugData;
    @interface ScaryBugDoc : NSObject
    @property (strong) ScaryBugData *data;
    @property (strong) UIImage *thumbImage;
    @property (strong) UIImage *fullImage;
    - (id)initWithTitle:(NSString*)title rating:(float)rating thumbImage:(UIImage *)thumbImage fullImage:(UIImage *)fullImage;
    @end

    没有特别需要注意的东西——我们只是设定了一些变量,属性,还有初始化函数。
    把 ScaryBugDoc.m 换为如下:
    #import "ScaryBugDoc.h"
    #import "ScaryBugData.h"
    @implementation ScaryBugDoc
    @synthesize data = _data;
    @synthesize thumbImage = _thumbImage;
    @synthesize fullImage = _fullImage;
    - (id)initWithTitle:(NSString*)title rating:(float)rating thumbImage:(UIImage *)thumbImage fullImage:(UIImage *)fullImage {
    if ((self = [super init])) {
    self.data = [[ScaryBugData alloc] initWithTitle:title rating:rating];
    self.thumbImage = thumbImage;
    self.fullImage = fullImage;
    }
    return self;
    }
    @end

    就这样,我们的数据模型就建立好了!现在我们可以弄一些示范数据然后在table view里把它们显示出来了。

    一个不一样的昆虫列表

    首先,我们要继续实现已有的Table View的子类来显示昆虫列表,而不仅仅是只显示一行文字(那是模版默认的初始状态)。
    在文件目录栏,打开MainStoryboard.storyboard。我们能在这里直观地看到我们程序所有视图的分布和程序的大致流程。如你所见,我们程序从一个导航控制器(navigation controller)开始(这个控制器让转换视图变得非常容易)。还有一个主控制器(master view controller)控制主视图,另外一个控制器控制细节视图(detail view)。
    【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
    点击选择那个主控制器,然后在左边的选择区域选择table view。在屏幕右边的属性栏,把Content设为Dynamic Prototypes。
    【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
    这样你就可以在故事版编辑器(Storyboard Editor)中随意改变table view cell的布局了,你也可以填入相应代码。我们只需要一个简单基本的cell,所以请如下图,在左边选择栏中点击选择那个空白的cell,然后在属性栏中把Style设为Basic。同时,把Identifier设为MyBasicCell。
    【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
    若你想了解如何创建自定义的cells,请阅读真篇关于Storyboard的文章Beginning Storyboards in iOS 5 Tutorial.
    好了,现在我们的Table View视图已经弄好了,我们需要编写代码让数据模型的内容能够正确的被载入到Table View中。
    我们会用NSMutableArray来储存ScaryBugDocs数据。NSMutableArray是一个和NSArray相似的数组。区别在于NSMutableArray可以根据需要,自动改变本身的大小。
    把下面的代码加入MasterViewController.h文件@interface和@end的两行之间:
    @property (strong) NSMutableArray *bugs;
    我们用这个变量属性来保存所有昆虫数据的数组。
    然后,请根据注释,对MasterViewController.m 文件进行如下改动:
    // 文件顶端
    #import "ScaryBugDoc.h"
    #import "ScaryBugData.h"
    // 加在 @implementation之后
    @synthesize bugs = _bugs;
    // 加在 viewDidLoad函数的末端
    self.title = @"Scary Bugs";
    // 把 shouldAutorotateToInterfaceOrientation 这个函数的返回值改为如下:
    return YES;
    // 把 tableView:numberOfRowsInSection 函数的返回值改为如下:
    return _bugs.count;
    // 把 tableView:cellForRowAtIndexPath 函数改为如下:
    - (UITableViewCell *)tableView:(UITableView *)tableView
    cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    UITableViewCell *cell = [tableView
    dequeueReusableCellWithIdentifier:@"MyBasicCell"];
    ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
    cell.textLabel.text = bug.data.title;
    cell.imageView.image = bug.thumbImage;
    return cell;
    }

    需要解释一下:

    首先,我们把title这个属性的值设为“Scary Bugs”。 title是个特别的,视图控制器自带的属性。导航控制器在显示一个视图时, 会把这个视图控制器的标题(title)呈现在屏幕上方标题栏中。 所以我们现在应该看到在标题栏中有”Scary Bugs“这个标题!
    下一步看看shouldAutorotateToInterfaceOrientation这个函数。 它的返回值总是YES。这说明我们将同时支持横向和竖向屏幕布局。由于这个类是UITableViewController的子类,我们不需要做任何其他工作,我们的table view会自动变换屏幕布局。
    下一步,因为我们的table view可以有任意多的行数,所以我们还需要重新实现numberOfSectionsInTableView和numberOfRowsInSection这两个函数, 这样系统在运行时才会知道总共需要在table view中显示多少部分, 每部分显示多少行。我们现在只有一个部分,所以我们不需要更改已有numberOfSectionsInTableView的函数模版,因为它的返回值已经是1了。而在numberOfRowsInSection函数中,我们只须返回昆虫数组的总长度。
    最后,我们实现tableView:cellForRowAtIndexPath这个函数。它应该是table view中最重要的函数。在这个函数中,我们构建table view每行所需要显示的cell的视图。操作系统每需要建立table view中的一行,就要调用这个函数一次。
    因为这个函数非常重要,让我们仔细分析一下这个函数:
    - (UITableViewCell *)tableView:(UITableView *)tableView
    cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    UITableViewCell *cell = [tableView
    dequeueReusableCellWithIdentifier:@"MyBasicCell"];
    ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
    cell.textLabel.text = bug.data.title;
    cell.imageView.image = bug.thumbImage;
    return cell;
    }

    第一行调用了dequeueReusableCellWithIdentifier函数。这是做什么呢?
    用这个函数的目的是为了优化性能。table view可能会有很多很多的行,但是屏幕上只能同时显示几行。所以在用户拉动table view时不断创建新的table cell是很没有效率的。我们可以通过回收利用那些已经不在屏幕内的已有的table cell来提高效率。这就是dequeueReusableCellWithIdentifier这个函数的作用。如果没有可以回收使用的cell了,系统才会根据你在Interface Builder中设计的cell来创建一个新的cell的对象。(还记得我们刚把它的identifier设为”MyBasicCell”吗?)。
    你可以选择使用默认的table view cell的样式,或者也可以自己创造设计table view cells。对我们来说默认的样式就可以了,所以我们只用将style属性设为UITableViewCellStyleDefault
    如果你对其他的table view cells的标准样式感兴趣,那就读读这一段苹果开发者目录Table View Programming Guide.
    最后,我设定了textLabel和imageView(这两个属性都包含在基本样式中)。
    这就是我们所需要的左右工作了!现在我们的table view只缺少一些示范性数据了。

    恐怖昆虫图片!

    当然了,一些恐怖虫子的图片是不可少的!你可以去网上自己下载一些,或者就用我为你准备的恐怖虫子图片 。这些图片是我随便找的.
    一旦你的图片都已经准备好了,就把它们拖到Project Navigator的根目录中。在弹出的选择栏中,勾选”Copy items into destination group’s folder (if needed)” 的选项(这个选项会把图片复制到你的程序根文件夹内,否则Xcode只会记录一个相对路径)。点击“Add“。
    【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
    然后打开ScaryBugsAppDelegate.m 并对其做如下改动:
    // 文件顶端
    #import "MasterViewController.h"
    #import "ScaryBugDoc.h"
    // application:didFinishLaunchingWithOptions函数一开始的地方
    ScaryBugDoc *bug1 = [[ScaryBugDoc alloc] initWithTitle:@"Potato Bug" rating:4 thumbImage:[UIImage imageNamed:@"potatoBugThumb.jpg"] fullImage:[UIImage imageNamed:@"potatoBug.jpg"]];
    ScaryBugDoc *bug2 = [[ScaryBugDoc alloc] initWithTitle:@"House Centipede" rating:3 thumbImage:[UIImage imageNamed:@"centipedeThumb.jpg"] fullImage:[UIImage imageNamed:@"centipede.jpg"]];
    ScaryBugDoc *bug3 = [[ScaryBugDoc alloc] initWithTitle:@"Wolf Spider" rating:5 thumbImage:[UIImage imageNamed:@"wolfSpiderThumb.jpg"] fullImage:[UIImage imageNamed:@"wolfSpider.jpg"]];
    ScaryBugDoc *bug4 = [[ScaryBugDoc alloc] initWithTitle:@"Lady Bug" rating:1 thumbImage:[UIImage imageNamed:@"ladybugThumb.jpg"] fullImage:[UIImage imageNamed:@"ladybug.jpg"]];
    NSMutableArray *bugs = [NSMutableArray arrayWithObjects:bug1, bug2, bug3, bug4, nil];
    UINavigationController * navController = (UINavigationController *) self.window.rootViewController;
    MasterViewController * masterController = [navController.viewControllers objectAtIndex:0];
    masterController.bugs = bugs;

    我们用ScaryBugDoc类的初始化函数来为四个昆虫创建数据。包括它们的名字,恐怖排名和图片。我们把它们都加到一个NSMutableArray(可变数组)中,并在table view中使用这个数组。
    我们知道RootViewController位于导航控制器堆栈的最表面,所以我们可以轻松的获得一个RootViewController的指针。我们也可以通过其他方法获得这个pointer,但我们现在的做法相对简单些。
    搞定,现在编译并运行你的程序后,你应该在table view中看到一系列恐怖虫子的照片。
    【iOS开发教程】iOS 5 APP开发系列教程(第一部分)
    [dl href=’http://vdisk.weibo.com/s/bSt97′]下载这个教程的代码[/dl]
    若上面的链接无法下载,请使用下面的备用下载
    [dl href=’http://pan.baidu.com/share/link?shareid=22683&uk=2502824232′]代码备用下载链接[/dl]

    本系列教程一览