iPhone开发基础教程笔记(四)--第五章 自动旋转和自动调整大小

第五章 自动旋转和自动调整大小

并非所有应用程序都支持自动旋转。苹果公司的一些iPhone应用程序仅支持单方向模式。例如,影片只能在横向模式下观看。

对于在纵向模式下启动的视图,其宽度和高度为320*460,如果没有状态栏,则高度为480px。横向模式下,宽度为480px,高度为

300或320px。
应用程序可以采用3种常用方法来管理旋转。
对于简单的界面,可以为界面中的所有对象指定正确的“自动调整”属性。在调整视图时,自动调整属性将通知iPhone视图中的控

件应该采用哪种行为方式。如果你曾经在Mac OS X上使用过Cocoa,那么应该熟悉这个基本过程,因为这也是用户在调整窗口时,指

定其中包含的Cocoa控件的行为方式的过程。
自动调整即快速又简单,但并非所有应用程序都适用。较复杂的界面必须采用不同的方式来处理自动旋转。一种是在看到视图旋转

提示时,手动调整视图中的对象位置。第二种方法是在IB中为视图设计两种不同版本,一种适用于纵向模式,一种适用于横向模式

。无论使用哪种方法,都需要覆盖UIViewController中的方法。

5.1 使用自动调整属性处理旋转
在Xcode中创建一个新项目,将其命名为Autosize。在IB中设计视图之前,需要告诉iPhone该视图支持自动旋转。可以通过修改视图

控制器类来实现这一点。

5.1.1 指定旋转支持
展开Classes文件夹,单击AutoSizeViewController.m。如果查看该文件中已有的代码,你会看到模版已经提供了一个名为

shouldAutorotateToInterfaceOrientation:的方法。现在该方法应该和如下所示的一样:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
 //Return YES for supported orientations
 return (interfaceOrientation==UIInterfaceOrientationPortrait);
}

系统通过调用此方法询问视图控制器是否应该旋转到指定方向。系统共定义了4种方向,分别对应握持iPhone的4种常见方式:
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
当电话的方向发生更改时,系统将调用此方法来操作视图控制器。interfaceOrientation参数将包含上述4个值之一,并且此方法需

要返回YES或NO,以指示是否应该旋转应用程序的窗口以匹配新的方向。

此方法的默认实现是检查interfaceOrientation是否与UIInterfaceOrientationPortrait相等,从而将应用程序限制为一种方向,

有效地禁用自动旋转。
如果要启动自动旋转。只需直接return YES即可。如果想支持其中一部分方向,则必须检查interfaceOrientation的值,对想要支

持的值返回YES,其余返回NO。一般苹果公司不建议支持UIInterfaceOrientationPortraitUpsideDown,因为接听来电时,电话仍然

处于倒置状态。

5.1.2 使用自动调整属性设计界面
展开Resources文件夹,双击AutoSzieViewController.xib。使用自动调整属性的好处在于,他们需要的代码极少。我们必须指定要

支持的方向。就像在视图控制器中的操作一样,但是实现此技术所需的其他所有工作将在IB中完成。
要了解如何指定方向,从库中拖出6个Round Rect Button 按钮,将他们放置在视图上。名称取1-6。排列方式为:
1 2
3 4
5 6
现在保存,运行,设置iPhone仿真器,从Hardware菜单中选择Rotate Left,将仿真器切换到横向模式。看看按钮的位置。
在默认情况下,大部分控件都会保持其与屏幕左侧和上侧的相对位置。这种设置适合某些控件,例如:按钮1,但其他按钮不是这样



5.1.3 自动调整属性
在IB中,单击按钮1,按Cmd+3调出大小检查器。大小检查器用于设置对象的自动调整属性。
检查器中Autosizing部分的左侧的框就是实际设置属性的地方,右侧的框是一个小动画,显示对象在调整大小时的行为方式。在左

侧的框中,内部的正方形表示当前的对象。
内部正方形中的红色箭头表示选定对象内部的水平和垂直空间。单击任意箭头都可以将实线变成虚线。实线表示可在调整窗口大小

时自由更改对象的宽度(高度);虚线表示iPhone会尽可能将对象的宽度(高度)保持为原始值。
内部正方形的四周的4个红色“I”表示选定对象的边与包含他的视图的同侧边之间的距离。虚线表示距离是灵活的,实线表示距离

的值是尽可能固定的。
实际操作一下,应该能够更容易理解。默认设置是 当调整包含对象的视图的大小时,对象的大小将保持不变,对象的左边和上边与

视图的对应边的距离也保持不变。
将所有线都变成虚线后,对象的大小保持不变,并且移动到父视图的中央。

5.1.4 设置按钮的自动调整属性
现在,设置6个按钮的自动调整属性。
按钮1为左和上实线,按钮2为右和上实线,按钮3为左实线,按钮4为右实线,按钮5为左下实线,按钮6为右下实线。
现在构建和运行应用程序。看看效果!

现在屏幕上还有大量未使用的空白空间。如果支持更改按钮的宽度和高度会更好一些。可以自由调整6个按钮的自动调整属性。多次

实践一下,你会适应自动调整属性的工作方式。
但是,你也会注意到,有时候没有哪种自动调整属性组合能够准备满足需要。这是就需要编写代码。

5.2 在旋转时重构视图
在IB中,将各按钮的宽度和高度,即w和h值更改为125.完成之后,使用蓝色引导线重新排列图标。现在看一下旋转屏幕时会发生什

么?旋转后按钮将会彼此重叠,因此在横向模式下,屏幕上没有足够的高度来容纳3个高度为125px的按钮。
我们可以使用自动调整属性来解决此问题,即允许更改按钮的高度。但是这样不能充分利用屏幕空间,因为屏幕中间存在很大的空

白空间。如果当界面处于纵向模式时,屏幕能容纳6个正方形按钮,那么在横向模式下也应该能容纳6个正方形按钮,我们只需将这

些按钮稍微移动一下。

5.2.1 声明和连接输出口
要更改控件的属性,我们需要通过输出口来指向要更改的对象。因此,我们需要为6个按钮分别声明一个输出口。

#import <UIKit/UIKit.h>
@interface AutosizeViewController:UIViewController{
IBOutlet UIButton *button1;
IBOutlet UIButton *button2;
IBOutlet UIButton *button3;
IBOutlet UIButton *button4;
IBOutlet UIButton *button5;
IBOutlet UIButton *button6;
}
@property (nonatomic,retain) UIView *button1;
@property (nonatomic,retain) UIView *button2;
@property (nonatomic,retain) UIView *button3;
@property (nonatomic,retain) UIView *button4;
@property (nonatomic,retain) UIView *button5;
@property (nonatomic,retain) UIView *button6;
@end

保存,并连接输出口。

5.2.2 在旋转时移动按钮
要移动按钮以便充分利用空间,需要覆盖AutosizeViewController中的

willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:。此方法在旋转开始之后,最后的旋转动画发生之前自动

调用。

说明:还可以使用另一个名为willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:的方法本章稍后使用它。在该

方法中的更改将在旋转动画发生之前完成,该方法是专为应该在旋转动画完成之前发生的更改而设计的。在本例中,我们需要按钮

在旋转完成的同时移动到他们的新位置,这就是我们选择second-half方法的原因。

添加以下代码:
#import "AutoSizeViewController.h"

@implementation AutosizeViewController
@synthesize button1;
@synthesize button2;
@synthesize button3;
@synthesize button4;
@synthesize button5;
@synthesize button6;

- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
  duration:(NSTimeInterval)duration {
 UIInterfaceOrientation toOrientation=self.interfaceOrientation;
 if (toOrientation==UIInterfaceOrientationPortrait||toOrientation==UIInterfaceOrientationPortraintUPsideDown)
 {
  button1.frame=CGRectMake(20,20,125,125);
  button2.frame=CGRectMake(175,20,125,125);
  button3.frame=CGRectMake(20,168,125,125);
  button4.frame=CGRectMake(175,168,125,125);
  button5.frame=CGRectMake(20,315,125,125);
  button6.frame=CGRectMake(175,315,125,125);
 } else {
  button1.frame=CGRectMake(20,20,125,125);
  button2.frame=CGRectMake(20,155,125,125);
  button3.frame=CGRectMake(177,20,125,125);
  button4.frame=CGRectMake(177,155,125,125);
  button5.frame=CGRectMake(328,20,125,125);
  button6.frame=CGRectMake(328,155,125,125);
 }
}

- (void) dealloc {
 [button1 release];
 [button2 release];
 [button3 release];
 [button4 release];
 [button5 release];
 [button6 release];
}

所有视图的大小和位置都在frame属性中指定。
保存并运行代码,查看效果。存在一个稍嫌怪异的地方。仔细观察将会发现,按钮直接跳转到了新的位置。是否可以制作动画呢?
借助Core Animation技术,可以在iPhone上轻松实现各种所需的动画类型。本书不打算直接使用Core Animation,但是Cocoa Touch

在后台大量使用了这种技术。在这里,我们的实现方式是将想要制作成动画的所有更改一起放置在“动画块”内部。为此,我们代

码添加了以下两行代码:

- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
  duration:(NSTimeInterval)duration {
 UIInterfaceOrientation toOrientation=self.interfaceOrientation;
 [UIView beginAnimations:@"move buttons" context:nil];
 if (toOrientation==UIInterfaceOrientationPortrait||toOrientation==UIInterfaceOrientationPortraintUPsideDown)
 {
  。。。
 } else {
  。。。
 }
 [UIView commitAnimations];
}

5.3 切换视图
在Xcode中,使用基于试图的应用程序模版创建一个新项目。将此项目命名为Swap。实际上,将在此应用程序中生成的界面还不够复

杂,不足以充分施展我们所使用的技术。但是,我们希望确保流程清晰,因此,我们将使用一个非常简单的界面。当编写的这个应

用程序启动时,他将处于纵向模式。应用程序中包含两个垂直排列的按钮。
Foo
Bar
当旋转电话时,我们将切换到横向模式下显示完全不同的视图。是横向排列的
Foo  Bar
单击按钮时,他们将被隐藏。这使我们有机会看到处理两个不同的输出口集合的细微差别。在实际应用中,可能有很多时候需要隐

藏或禁用某个按钮。

5.3.1 确定输出口
因为我们将在每个视图上构建两个按钮,而且一个输出口不能指向多个对象,所以我们需要声明4个输出口。
我们还需要两个输出口来指向两个不同的视图版本。当只有一个视图时,我们只需要使用父类的view属性。但是,因为我们需要在

运行时更改view的值,所以需要确保能够获得两种视图,因此需要两个UIView的输出口。

5.3.2 确定动作
声明一个buttonPressed:动作。

5.3.3 声明动作和输出口
#import <UIKit/UIKit.h>
#define degreesToRadian(x) (M_PI * (x)/180.0)

@interface SwapViewController : UIViewController {
 IBOutlet UIView *landscape;
 IBOutlet UIView *portrait;
 
 //Foo
 IBOutlet UIButton *landscapeFooButton;
 IBOutlet UIButton *portraitFooButton;

 //Bar
 IBOutlet UIButton *landscapeBarButton;
 IBOutlet UIButton *portraitBarButton;
}
@property (nonatomic,retain) UIView *landscape;
@property (nonatomic,retain) UIView *portrait;

@property (nonatomic,retain) UIButton *landscapeFooButton;
@property (nonatomic,retain) UIButton *portraitFooButton;

@property (nonatomic,retain) UIButton *landscapeBarButton;
@property (nonatomic,retain) UIButton *portraitBarButton;

- (IBAction)buttonPressed:(id)sender;
@end

5.3.4 设计两个视图
在我们的nib文件中,我们不需要作为模版一部分提供的View图标,因为他的大小无法更改,于是单击View图标,并删除。接下来从

库中拖出两个UIView。并重命名。单击View,等待几秒钟,然后单击图标的名称。等待图标的名称变为可编辑状态,然后可以输入

新名称。将两个视图分别命名为Portrait和Landscape
现在,按住Control键并将File's Owner拖到Portrait图标,在弹出灰色的菜单中选择Portrait输出口。同样的操作对Landscape。
现在再次按住Ctrl键,并将File's Owner拖到Portrait图标,选择View输出口,指定应该在启动时显示的视图。
双击Landscape图标,按下Cmd+3调出大小检查器。现在,此视图的高和宽分别是460*320.将高和宽分别更改为300*480.现在,从库

中拖出两个Round Rect Button到Landscape视图中。按钮的准确大小和位置无关紧要。设置大小为125*125,命名Foo和Bar。
连接输出口和操作。
然后双击Portrait图标,打开他,从库中拖出两个Round Rect Button。命名,连接输出口和操作。
保存

5.3.5 实现交换和动作
现在来编写代码来处理交换过程和点击按钮操作。将以下代码添加到SwapViewController.m中:

#import "SwapViewController.h"

@implementation SwapViewController
@synthesize landscape;
@synthesize portrait;
@synthesize landscapeFooButton;
@synthesize portraitFooButton;
@synthesize landscapeBarButton;
@synthesize portraitBarButton;

- (void)willAnimateFirstHaldOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
   duration:(NSTimeInterval)duration {
 if (toOrientation==UIInterfaceOrientationPortrait)
 {
  self.view=self.portrait;
  self.view.transform=CGAffineTransformIdentity;
  self.view.transform=CGAffineTransformMakeRotation(degreesToRadian(0));
  self.view.bounds=CGRectMake(0.0,0.0,300.0,480.0);
 }
 else if (toOrientation==UIInterfaceOrientationLandscapeLeft)
 {
  self.view=self.landscape;
  self.view.transform=CGAffineTransformIdentity;
  self.view.transform=CGAffineTransformMakeRotation(degreesToRadian(-90));
  self.view.bounds=CGRectMake(0.0,0.0,460.0,320.0);
 } 
 else if (toOrientation==UIInterfaceOrientationPortraitUpsideDown)
 {
  self.view=self.portrait;
  self.view.transform=CGAffineTransformIdentity;
  self.view.transform=CGAffineTransformMakeRotation(degreesToRadian(180));
  self.view.bounds=CGRectMake(0.0,0.0,300.0,480.0);
 }
 else if (toOrientation==UIInterfaceOrientationLandscapeRight)
 {
  self.view=self.landscape;
  self.view.transform=CGAffineTransformIdentity;
  self.view.transform=CGAffineTransformMakeRotation(degreesToRadian(90));
  self.view.bounds=CGRectMake(0.0,0.0,460.0,320.0);
 } 
}

- (IBAction)buttonPressed:(id)sender {
 if (sender==portraitFooButton || sender==landscapeFooButton )
 { 
   portraitFooButton.hidden=YES;
   landscapeFooButton.hidden=YES;
 }
 else 
 {
   portraitBarButton.hidden=YES;
   landscapeBarButton.hidden=YES;
 }
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
 return YES;
}

- (void)dealloc {
 [landscape release];
 [portrait release];
 [landscapeFooButton release];
 [[portraitFooButton release];
 [landscapeBarButton release];
 [[portraitBarButton release];
 
 [super dealloc];
}

@end

willAnimateFirstHaldOfRotationToInterfaceOrientation:duration:这个方法来自我们覆盖的一个超类,这个超类在旋转开始之

后与旋转实际发生之前被调用。我们在此方法中采用的动作将是旋转动画的前半部分。
在此方法中,我们看一下旋转的目标方向,并根据新方向将view属性设置为landscape或portrait。然后调用

CGAffineTransformMakeRotation(Core Graphics框架的一部分)来创建一个“旋转变换”。变换是对对象大小、位置或角度的更改

的数学描述。一般情况下,iPhone在旋转电话时自动设置变换值。但是在这里,当切换到新视图时,我们必须确保为其提供了正确

的值,以不至于使iPhone混淆。这就是willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:方法在每次设置视图

的transform属性时所做的工作。旋转视图之后,调整其框架,使其与处于当前方向的窗口完美结合。

现在,我们是否可以编译应用程序并使用它?还不行!在编译之前,我们还需要做一件事情,请在继续之前务必先保存代码。

5.3.6 连接Core Graphics框架
CGAffineTransformMakeRotation函数是Core Graphics框架的一部分。但是,在默认情况下,Core Graphics框架未包含在Xcode项

目中。我们需要手动将该框架链接到项目中。使用项目窗口中Groups&Files窗格里的Frameworks文件夹完成此任务。
现在,我们如何知道需要链接到Core Graphics框架?在Xcode中,从Build菜单中选择Build,然后会看到

CGAffineTransformMakeRotation出现了一个链接错误。我们知道,“CG”指的是“Core Graphics”,但是你并非每次都知道哪个

框架包含链接错误中报告的方法。这里有一个技巧可以帮助找到出现问题的函数的框架。
首先,在源代码中找到CGAffineTransformMakeRotation调用。双击函数名称,并将光标停留在选定函数上面。稍等片刻,函数名称

右侧将出现一个下拉菜单箭头。将光标向右滑动到箭头处并单击它。从出现的上下文菜单选择Jump to Definition。
然后,我们将转到定义该函数或方法的头文件。一般而言,头文件顶部将指示他所属的框架。以下是定义

CGAffineTransformMakeRotation的头文件顶部:

/* CoreGraphics - CGAffineTransform.h
 ...

这里的注释通常包含头文件的文件名和所属框架。
知道了需要链接的库或框架之后,下一步就是找到他。
在Groups&Files窗格中单击Frameworks文件夹,从Project菜单中选择Add to Project。这将打开标准的Open File 对话框。我们现

在需要导航到正确的Core Graphics框架版本:适用于iPhone仿真器的版本。
要找到所需的框架版本,我们需要查看Mac的硬盘上的/Developer文件夹。该文件夹包含一个Platforms文件夹,而

iPhoneSimulator.platform就在Platforms文件夹中。但是,找到该文件夹还不够。
在iPhoneSimulator.platform文件夹内有一个名为Developer的文件夹,Developer文件夹包含SDKs文件夹。在SDKs文件夹内是

iPhoneSimulator2.1.sdk文件夹,其中又包含一个System文件夹。在System文件夹内是Library文件夹,而Library文件夹内是

Frameworks文件夹。在Frameworks文件夹内是一个名为CoreGraphics.framework的框架,这就是我们需要的框架。
如果你在自己的计算机上执行这些步骤,则会注意到CoreGraphics.framework不仅仅是一个文件。在此框架下有许多项可供选择。

不要继续展开文件夹,只需选择CoreGraphicsframework并单击add按钮。
在本书中,我们将在许多项目中添加框架。你可能想在此处贴上一个书签。以便在以后添加框架时可以参考本小节提供的文件路径


综合下路径:
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDks/iPhoneSimulator2.1.sdk/System/Library/Frameworks/CoreG

raphics.framework/
注意到最后的路径不要再展开文件夹。直接添加。

在项目添加图像时,也会出现相同的文件夹。但在这里,选择正确的选项要重要的多。我们不想将该框架添加到项目中,因为我们

只是进行链接,因此需要确保没有选中Copy items into destination group's folder(if needed)复选框。
更重要的是,我们需要确保选择了正确的Reference Type,那就是Relative to Current SDK。此选项支持在更改所使用的SDK时更

改选定的框架。iPhone和iPhone仿真器都有自己的SDK,我们将在为iPhone自身构建应用程序时更改SDK。选定此选项后,更改SDK将

会自动更改我们链接到的框架版本。

说明:非常棒!如果你申请并加入了苹果公司的iPhone Developer Program,那么你想要做的第一件事就是构建应用程序,并在

iPhone或iPod上运行他们。很棒的地方在于,在更改平台时无需更改框架。Xcode将自动进行正确操作。你只需在将框架添加到项目

时,确保选择了Relative to Current SDK。

选择了正确的选项之后,单击Add按钮,将项目链接到Core Graphics框架。
现在可以编译并运行项目了!

提示:添加框架的一种更安全的方式是,右键单击Groups&Files窗格中的Resources。这样会弹出一个上下文菜单,其Add子菜单中

包含一个Existing Frameworks...选项。选择此选项与从Project菜单选择Add to Project的效果完全一样,但此选项知道你不想将

框架复制到项目中。


5.4 小结
本章介绍了在应用程序中支持自动旋转的3种完全不同的方法。自动调整属性、编写代码在旋转电话时重构试图,以及在旋转电话时如何在两个完全不同的视图之间切换,还学习了如何将新框架链接到应用程序中。
在下一章将介绍真实的多视图应用程序。目前为止,我们编写的每个应用程序都仅使用了一个视图控制器和一个内容视图。然后许多复杂的iPhone应用程序(例如Mail和Contracts)必须使用多个视图和视图控制器才能实现,我们将在下一章介绍如何使用多个视图和视图控制器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值