构建iOS应用:从基础到实践
1. 示例iOS应用概述
我们将通过一个完整的iOS应用示例来深入了解iOS应用开发。这个示例应用模拟了一个索引卡列表,对于学生备考或学习外语新单词非常有帮助。应用的主用户界面(UI)很简单,有用于切换上一张和下一张索引卡的按钮,还有一个显示卡片隐藏信息的选项。此外,该应用还支持为每张索引卡播放声音文件,这对记忆很有帮助。
1.1 主要文件介绍
应用的主要文件包括主文件、主视图控制器接口和实现文件等。下面我们将详细介绍这些文件。
1.1.1 主文件
主文件负责初始化Cocoa Touch库并运行主循环。可以使用
UIApplicationMain
来完成这个任务,同时需要确保应用的自动释放池已初始化,这可以通过在
NSAutoreleasePool
类上调用
alloc/init
来实现。以下是主文件的代码:
//
// main.m
#import <UIKit/UIKit.h>
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
1.1.2 主视图控制器接口
视图控制器的职责是确定其对应视图如何响应指向它的事件。
MainViewController
的接口声明了一些插槽(使用
IBOutlet
关键字)和动作(使用
IBAction
关键字),以便在新事件发生时,UI可以向该对象发送消息。以下是
MainViewController
接口的代码:
#import "FlipsideViewController.h"
@interface MainViewController : UIViewController <FlipsideViewControllerDelegate>
{
IBOutlet UIView *wordView;
IBOutlet UILabel *lbWord;
IBOutlet UILabel *lbTranslation;
IBOutlet UIButton *btBuy;
IBOutlet UIButton *btPlay;
IBOutlet UIView* ad;
}
- (IBAction)showInfo:(id)sender;
- (IBAction)playSound:(id)sender;
- (IBAction)showPrevious:(id)sender;
- (IBAction)showNext:(id)sender;
- (IBAction)shuffle:(id)sender;
- (IBAction)review:(id)sender;
- (IBAction)buyClicked:(id)sender;
+ (void)syncIsFreeVersion:(BOOL)setValue;
+ (void)refreshTopLevelViewController;
@end
1.1.3 主视图控制器实现
MainViewController
的实现文件定义了主视图控制器如何与系统交互。该文件实现了从UI调用的所有动作,如切换到下一张和上一张卡片、显示卡片内容以及播放声音等。以下是部分代码:
//
// file: MainViewController.m
#import "MainViewController.h"
#import "InfoView.h"
#import "PurchaseController.h"
#import <AVFoundation/AVFoundation.h>
@implementation MainViewController
int currentPos = 0;
struct words
{
NSString *word, *translation, *file;
} words[] = {
{ @"Ajuda", @"Help", nil },
{ @"Almoço", @"Lunch" , @"Almoco"},
{ @"Restaurante", @"Restaurant", nil},
{ @"Rua", @"Street", nil},
{ @"Semana", @"Week", nil},
{ @"Supermercado", @"Supermarket", nil},
{ @"Viagem", @"Travel", nil},
};
#define maxWords sizeof(words)/sizeof(*words)
int perm[maxWords];
static MainViewController *ui_controller = nil;
- (void) refreshWord
{
CGFloat d = 0.9;
[UIView animateWithDuration:d animations:^{ wordView.alpha = 0.0; }
completion:^(BOOL finished){
int pos = perm[currentPos];
lbWord.text = words[pos].word;
lbTranslation.text = words[pos].translation;
lbTranslation.hidden = YES;
}];
[UIView animateWithDuration:d animations:^{ wordView.alpha = 1.0; }
completion:nil];
}
- (IBAction)showNext:(id)sender
{
currentPos = (currentPos + 1 == maxWords) ? 0 : currentPos + 1;
[self refreshWord];
}
- (IBAction) showPrevious:(id)sender
{
currentPos = (currentPos == 0) ? maxWords - 1 : currentPos - 1;
[self refreshWord];
}
1.2 方法详解
1.2.1
refreshWord
方法
该方法负责通过过渡效果重绘当前视图。使用
animateWithDuration:animations:completion:
方法进行简单动画,通过改变视图的透明度(
alpha
值)实现淡入淡出效果,最后显示当前单词的定义。
1.2.2
showNext
和
showPrevious
方法
这两个方法用于控制索引卡列表中的当前位置。
showNext
方法移动到列表中的下一张可用索引卡,
showPrevious
方法返回上一张可用位置。它们都使用
refreshWord
方法的淡入淡出效果重绘屏幕。
1.2.3
makeLabel
方法
该方法创建并返回一个新的
UILabel
对象。首先使用传入的参数初始化
CGRect
结构,确定控件的位置和尺寸,然后创建一个新的
UILabel
,并设置其标题、对齐方式和背景颜色。以下是代码:
- (UILabel*) makeLabel:(NSString*)title width:(CGFloat)w
height:(CGFloat)h top:(CGFloat)t
{
CGFloat vw = wordView.frame.size.width;
CGRect r = CGRectMake(vw/2 - w/2, t, w, h);
UILabel *l = [[[UILabel alloc] initWithFrame:r] autorelease];
l.text = title;
l.textAlignment = UITextAlignmentCenter;
l.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.0];
return l;
}
1.2.4
resetAdPosition
方法
在移动应用中,显示广告以实现盈利是常见任务。
resetAdPosition
方法展示了如何使用Cocoa Touch UI类轻松显示广告。具体步骤如下:
1. 获取现有广告对象的框架,该对象是在Xcode设计时添加的。
2. 获取内容窗口的框架矩形,以便使用其尺寸调整广告控件。
3. 设置广告控件的框架属性,将其移动到工具栏上方的位置。
4. 广告单元将自动显示,内容由Apple直接提供。
以下是代码:
- (void)resetAdPosition:(UIView *)adView
{
CGRect adrect = [adView frame];
AppDelegate *deleg = [[UIApplication sharedApplication] delegate];
UIWindow *viewCont = deleg.window;
// get the content frame
CGRect brect = viewCont.frame;
// move to location above the toolbar
adrect.origin.y = brect.size.height - adrect.size.height - 20;
adView.frame = adrect;
NSLog(@" height is %lf, y for bar is %lf", adrect.origin.y, brect.origin.y);
}
1.3 视图加载和其他方法
1.3.1
viewDidLoad
方法
该方法在控制器加载其资源(包括nib文件)后,但在UI显示在屏幕之前调用。具体操作如下:
1. 调用父类的
viewDidLoad
方法。
2. 初始化
ui_controller
变量。
3. 调用
resetAdPosition
方法调整广告位置。
4. 调用
refreshUi
方法。
5. 使用
srand
函数初始化随机数生成器。
6. 初始化排列数组
perm
,使索引卡最初按标准顺序显示。
7. 调用
refreshWord
方法绘制所需的索引卡。
1.3.2
shuffle
方法
该方法用于打乱索引卡的显示顺序。通过创建卡片的随机排列来实现,具体步骤如下:
1. 遍历
perm
数组的所有位置。
2. 生成一个随机值并存储在
pos
变量中。
3. 将当前位置的值与随机位置的值进行交换。
4. 调用
refreshWord
方法重绘视图。
以下是代码:
- (IBAction)shuffle:(id)sender
{
for (int i=0; i<maxWords; ++i)
{
int pos = (int)((rand() % ((maxWords-i) * 10))/ 10.0);
int t = perm[i];
perm[i] = perm[i+pos];
perm[i+pos] = t;
}
[self refreshWord];
}
1.3.3
playSound
方法
该应用的一个特性是能够为每张索引卡播放声音。
playSound
方法的具体操作如下:
1. 从
words
数据结构中检索要播放的声音文件的名称。
2. 如果该位置没有存储文件名,则根据索引卡中的信息生成文件名。
3. 使用
pathForResource:ofType:
方法获取声音文件的存储路径。
4. 创建一个
NSURL
对象表示该文件。
5. 创建一个
AVAudioPlayer
对象,用声音资源URL初始化。
6. 设置播放器选项,如音量和循环次数,并设置代理。
7. 发送
play
消息开始播放声音文件。
以下是代码:
- (IBAction)playSound:(id)sender
{
NSLog(@"Playing a sound...");
AVAudioPlayer *player;
NSError *playerError = nil;
NSString *file_name = words[perm[currentPos]].file;
if (file_name == nil)
{
file_name = words[perm[currentPos]].word;
}
file_name = [file_name lowercaseString];
NSString *path = [[NSBundle mainBundle] pathForResource:file_name ofType:@"mp3"];
if (!path)
{
NSLog(@"Error Loading File");
return;
}
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: path];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&playerError];
if (!player || playerError)
{
NSLog(@"Error creating player %@: %@", file_name,
[playerError localizedDescription]);
return;
}
player.volume = 1.0;
player.numberOfLoops = 0;
player.delegate = (id<AVAudioPlayerDelegate>)self;
NSLog(@"calling play method...");
[player play];
[fileURL release];
}
1.3.4
audioPlayerDidFinishPlaying
方法
该方法是
AVAudioPlayerDelegate
接口的一部分,当播放器对象通知其代理播放完成时调用。在实现中,需要释放播放器对象,以避免内存泄漏。以下是代码:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(@"Releasing player...");
[player release];
}
1.3.5
reveal
方法
该方法用于显示当前索引卡的隐藏值。通过更改
UILabel
对象的
hidden
属性,Cocoa Touch库将自动更新UI。以下是代码:
- (IBAction)reveal:(id)sender
{
lbTranslation.hidden = NO;
}
1.3.6
flipsideViewControllerDidFinish
方法
该方法负责在应用指示需要关闭模态视图时执行关闭操作。通过调用继承的
dismissModalViewControllerAnimated:
方法实现,可选择是否使用动画效果。以下是代码:
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)c
{
[self dismissModalViewControllerAnimated:YES];
}
1.3.7
showInfo
方法
该方法用于显示一个信息框,展示应用的一些信息,如名称和当前使用的选项。具体步骤如下:
1. 创建一个新的
InfoView
对象,使用
initWithNibName:bundle:
方法初始化。
2. 设置模态过渡样式为水平翻转效果。
3. 显示模态视图,并可选择是否使用动画。
以下是代码:
- (IBAction)showInfo:(id)sender
{
InfoView *c = [[[InfoView alloc] initWithNibName:@"InfoView" bundle:nil] autorelease];
c. modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:c animated:YES];
}
1.3.8
refreshTopLevelViewController
方法
该方法作为一个辅助方法,用于从应用的其他部分刷新UI控制器。使用静态变量
ui_controller
,由于向
nil
对象发送消息是有效的,因此在使用前无需检查其是否已初始化。以下是代码:
+ (void) refreshTopLevelViewController
{
[ui_controller refreshUi];
}
1.3.9
shouldAutorotateToInterfaceOrientation
方法
当iOS设备尝试更改方向时,会调用该方法。应用可以根据需要响应方向更改。此示例应用仅支持竖屏方向,只有当屏幕方向为
UIInterfaceOrientationPortrait
时返回
YES
。以下是代码:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io
{
// Return YES for supported orientations.
return (io == UIInterfaceOrientationPortrait);
}
1.3.10
didReceiveMemoryWarning
方法
当系统内存不足时,会向视图发送该方法。通过响应此方法,应用可以在资源使用问题发生时迅速恢复。此示例应用仅调用父类的相同方法,对于更复杂的应用,需要考虑在低内存场景下如何减少资源使用。以下是代码:
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
1.3.11
viewDidUnload
方法
当当前视图从屏幕上卸载时,该方法会释放任何未使用的内存。这对于移动系统非常重要,因为内存资源有限。通过释放当前未使用的内存,可以供其他应用(甚至同一应用的其他视图)使用,同时减少系统所需的总资源。以下是代码:
- (void)viewDidUnload
{
[btBuy release];
btBuy = nil;
[btPlay release];
btPlay = nil;
[super viewDidUnload];
}
1.3.12
dealloc
方法
该方法负责将类使用的资源返回给操作系统。由于该类使用了一些控件,因此需要在此时释放它们,以避免不必要的内存泄漏。同时,务必调用父类的
dealloc
方法,否则父类管理的资源会出现内存泄漏。以下是代码:
- (void)dealloc
{
[btBuy release];
[btPlay release];
[super dealloc];
}
1.4 反面视图控制器
1.4.1 反面视图控制器接口
FlipsideViewController
是
UIViewController
的子类,用于响应反面控件发送的消息,显示应用的辅助信息。该控制器的主要区别在于使用了一个委托对象,声明为
FlipsideViewControllerDelegate
类型。该协议用于与
MainWindowViewController
通信,只有一个方法
flipsideViewControllerDidFinish:
。以下是代码:
// file FlipsideViewController.m
#import <UIKit/UIKit.h>
@protocol FlipsideViewControllerDelegate;
@interface FlipsideViewController : UIViewController
{
}
@property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
@end
// this protocol must be implemented by classes passed as a delegate
@protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
@end
1.4.2 反面视图控制器实现
FlipsideViewController
的实现文件提供了一些方法,用于响应指向反面视图的消息。以下是部分代码:
// file FlipsideViewController.m
#import "FlipsideViewController.h"
@implementation FlipsideViewController
@synthesize delegate=_delegate;
- (void)viewDidLoad
{
// the view is now loaded, call the super class first
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
}
- (void)viewDidUnload
{
// make sure that the parent class is unloaded
[super viewDidUnload];
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Actions
- (IBAction)done:(id)sender
{
// notifies the delegate about the action of closing the current view
[self.delegate flipsideViewControllerDidFinish:self];
}
@end
1.5 总结
通过这个示例应用,我们学习了如何使用Objective-C创建在iPhone和iPad设备上运行的移动应用。Cocoa框架是iOS应用的官方开发环境,如果你打算为苹果平台开发移动应用,Objective-C是最快获得可用产品的途径。
我们了解了iOS应用与桌面操作系统应用的区别,这些区别表明需要定制的Objective-C框架集来控制移动应用的特定硬件和软件要求。同时,我们还讨论了Cocoa Touch提供的一些支持,包括视图、窗口和基于触摸的UI控件。
以下是部分方法的功能总结表格:
| 方法名 | 功能 |
| ---- | ---- |
|
refreshWord
| 重绘当前视图,实现淡入淡出效果 |
|
showNext
| 切换到下一张卡片 |
|
showPrevious
| 切换到上一张卡片 |
|
playSound
| 播放当前卡片的声音文件 |
|
reveal
| 显示当前卡片的隐藏信息 |
下面是
playSound
方法的流程图:
graph TD;
A[开始] --> B[获取声音文件名];
B --> C{文件名是否为空};
C -- 是 --> D[根据卡片信息生成文件名];
C -- 否 --> E[使用现有文件名];
D --> F[获取文件路径];
E --> F;
F --> G{路径是否存在};
G -- 是 --> H[创建NSURL对象];
G -- 否 --> I[输出错误信息并返回];
H --> J[创建AVAudioPlayer对象];
J --> K[设置播放器选项];
K --> L[播放声音文件];
L --> M[释放NSURL对象];
M --> N[结束];
通过以上内容,我们对iOS应用开发有了更深入的了解,希望这些知识能帮助你开始自己的Objective-C开发之旅。
2. 应用开发要点与实战经验
2.1 数据结构与存储
在这个示例应用中,使用了简单的结构体数组来存储单词和翻译信息:
struct words
{
NSString *word, *translation, *file;
} words[] = {
{ @"Ajuda", @"Help", nil },
{ @"Almoço", @"Lunch" , @"Almoco"},
{ @"Restaurante", @"Restaurant", nil},
{ @"Rua", @"Street", nil},
{ @"Semana", @"Week", nil},
{ @"Supermercado", @"Supermarket", nil},
{ @"Viagem", @"Travel", nil},
};
这种方式对于简单应用来说足够,但对于更复杂的应用,可能需要使用数据库来存储这些信息。以下是使用数据库存储的一般步骤:
1.
选择数据库
:如SQLite或Core Data。
2.
创建数据库和表
:定义表结构,包含单词、翻译和声音文件等字段。
3.
插入数据
:将单词和翻译信息插入到数据库中。
4.
查询数据
:根据需要从数据库中查询数据。
2.2 动画效果的实现
在
refreshWord
方法中,使用了
UIView
的动画方法来实现淡入淡出效果:
- (void) refreshWord
{
CGFloat d = 0.9;
[UIView animateWithDuration:d animations:^{ wordView.alpha = 0.0; }
completion:^(BOOL finished){
int pos = perm[currentPos];
lbWord.text = words[pos].word;
lbTranslation.text = words[pos].translation;
lbTranslation.hidden = YES;
}];
[UIView animateWithDuration:d animations:^{ wordView.alpha = 1.0; }
completion:nil];
}
动画效果可以提升用户体验,以下是实现动画效果的一般步骤:
1.
选择动画类型
:如淡入淡出、缩放、旋转等。
2.
设置动画参数
:如动画持续时间、延迟时间等。
3.
定义动画块
:在动画块中设置视图的属性变化。
4.
设置完成回调
:在动画完成后执行一些操作。
2.3 声音播放功能
playSound
方法实现了声音播放功能,具体步骤如下:
1.
获取声音文件名
:从
words
数据结构中获取。
2.
生成文件路径
:使用
pathForResource:ofType:
方法。
3.
创建
NSURL
对象
:表示声音文件。
4.
创建
AVAudioPlayer
对象
:并初始化。
5.
设置播放器选项
:如音量、循环次数等。
6.
播放声音文件
:调用
play
方法。
以下是
playSound
方法的代码回顾:
- (IBAction)playSound:(id)sender
{
NSLog(@"Playing a sound...");
AVAudioPlayer *player;
NSError *playerError = nil;
NSString *file_name = words[perm[currentPos]].file;
if (file_name == nil)
{
file_name = words[perm[currentPos]].word;
}
file_name = [file_name lowercaseString];
NSString *path = [[NSBundle mainBundle] pathForResource:file_name ofType:@"mp3"];
if (!path)
{
NSLog(@"Error Loading File");
return;
}
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: path];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&playerError];
if (!player || playerError)
{
NSLog(@"Error creating player %@: %@", file_name,
[playerError localizedDescription]);
return;
}
player.volume = 1.0;
player.numberOfLoops = 0;
player.delegate = (id<AVAudioPlayerDelegate>)self;
NSLog(@"calling play method...");
[player play];
[fileURL release];
}
2.4 广告显示与布局
resetAdPosition
方法展示了如何在应用中显示广告:
- (void)resetAdPosition:(UIView *)adView
{
CGRect adrect = [adView frame];
AppDelegate *deleg = [[UIApplication sharedApplication] delegate];
UIWindow *viewCont = deleg.window;
// get the content frame
CGRect brect = viewCont.frame;
// move to location above the toolbar
adrect.origin.y = brect.size.height - adrect.size.height - 20;
adView.frame = adrect;
NSLog(@" height is %lf, y for bar is %lf", adrect.origin.y, brect.origin.y);
}
显示广告的步骤如下:
1.
获取广告视图的框架
:确定其位置和大小。
2.
获取内容窗口的框架
:用于调整广告位置。
3.
调整广告位置
:将广告移动到合适的位置。
4.
更新广告视图的框架
:使调整生效。
2.5 视图控制器的交互
MainViewController
和
FlipsideViewController
之间通过委托模式进行交互。
FlipsideViewController
在关闭时通知
MainViewController
:
- (IBAction)done:(id)sender
{
// notifies the delegate about the action of closing the current view
[self.delegate flipsideViewControllerDidFinish:self];
}
委托模式的使用步骤如下:
1.
定义委托协议
:包含需要实现的方法。
2.
在委托对象中设置委托属性
:指向委托者。
3.
在委托者中实现委托协议的方法
:处理委托事件。
2.6 资源管理与内存优化
在移动应用开发中,资源管理和内存优化非常重要。以下是一些关键方法的作用:
-
didReceiveMemoryWarning
:当系统内存不足时调用,可释放一些不必要的资源。
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
-
viewDidUnload:当视图卸载时调用,释放未使用的内存。
- (void)viewDidUnload
{
[btBuy release];
btBuy = nil;
[btPlay release];
btPlay = nil;
[super viewDidUnload];
}
-
dealloc:在对象销毁时调用,释放所有使用的资源。
- (void)dealloc
{
[btBuy release];
[btPlay release];
[super dealloc];
}
2.7 应用方向支持
shouldAutorotateToInterfaceOrientation
方法控制应用是否支持设备方向变化:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)io
{
// Return YES for supported orientations.
return (io == UIInterfaceOrientationPortrait);
}
此示例应用仅支持竖屏方向,可根据需要修改返回值以支持其他方向。
2.8 总结与展望
通过这个示例应用,我们全面了解了iOS应用开发的各个方面,包括数据存储、动画效果、声音播放、广告显示、视图控制器交互、资源管理和方向支持等。Objective-C结合Cocoa框架为我们提供了强大的开发能力,能够创建出功能丰富、用户体验良好的移动应用。
在未来的开发中,我们可以进一步扩展这个应用,例如:
-
增加更多的单词和翻译
:丰富学习内容。
-
优化动画效果
:提升用户体验。
-
集成更多的功能
:如在线学习、社交分享等。
以下是部分资源管理方法的总结表格:
| 方法名 | 功能 |
| ---- | ---- |
|
didReceiveMemoryWarning
| 响应系统内存不足警告,释放资源 |
|
viewDidUnload
| 视图卸载时释放未使用的内存 |
|
dealloc
| 对象销毁时释放所有资源 |
下面是资源管理的流程图:
graph TD;
A[系统内存不足] --> B[调用didReceiveMemoryWarning];
B --> C{是否有可释放资源};
C -- 是 --> D[释放资源];
C -- 否 --> E[无操作];
F[视图卸载] --> G[调用viewDidUnload];
G --> H[释放未使用内存];
I[对象销毁] --> J[调用dealloc];
J --> K[释放所有资源];
希望这些知识和经验能帮助你在iOS应用开发的道路上取得更好的成果,不断探索和创新,开发出更优秀的应用。
超级会员免费看
2955

被折叠的 条评论
为什么被折叠?



