這篇的內容是切換Views,也是上一篇中提到的第三種當iphone發生旋轉後改變布局的方式,先回顧一下上一篇中提到的三種方式
1、使用Autosizing
2、寫code
3、重新弄個View,替換原先的View
切換View,顧名思義就是在兩個不同的View中間進行切換,那麼我們至少需要有2個View,一個View展現當豎著(Portrait)拿iphone時的介面,另一個View展現當橫著(Landscape)拿iphone是的介面,當我們旋轉iphone時,就在這2個View之間進行切換,給使用者的感覺好像是用一個介面,其實我們是用2個View在進行替換。這樣做的好處是不必處理複雜的控制項重新布局問題,但是壞處是因為是2個不同的View,我們必須用2套控制項,然後當一個控制項進行改變時,在另一個View中的“相同”控制項也應進行改變(例如在一個View中被隱藏了,那在另一個View中也應該被隱藏,因為是同一個介面嘛,這點很重要。)
好,廢話少說,開始這篇的學習。
1)建立一個新的Single View項目,並命名為Swap
2)添加2個button
添加2個button,分別命名為Foo和Bar,長寬都為125,並像一樣進行布局
3)添加另一個View(landscape view)
由於這個view是當前view(portrait view)的橫向版本,其介面上的控制項類型、個數、功能應該和protrait view一樣,只是在布局上有些不同,因此最簡便的方法便是先複製一個portrait view,然後對介面上的控制項位置大小從新布局。
選中BIDViewController.xib,在xib的editor dock中找到View
按住鍵盤上的option鍵,滑鼠選中View並拖動滑鼠,有一個綠色的加號出現,然後在View的同一層的下面放開滑鼠,這樣一個View就複製好了。
(可能2個View重疊在一起,用滑鼠移動上面的一個View,就會看到有2個View了)
在editor dock中選中新加的View,然後切換到Attributes inspector,找到Simulated Mertrics欄中的Orientation,將其屬性改成Landscape,這樣View就橫過來了
但是另一個button不見了,因為button位置的原因,另一個button沒有顯示在View中,我們現在editor dock中選中看不見的那個button
然後在Size inspector中將其起始點設成10,10
看不見的那個button出現了
重新對其布局
4)建立View的Outlet
因為我們要切換View,因此必須指定View的Outlet,這樣我們就可以在代碼中對View進行操作了,建立View的Outlet的方法和建立其他控制項的Outlet的方法一樣,按下control鍵,滑鼠選中View,拖動到BIDViewController.h中釋放,並命名即可。我們首先添加Portrait View的Outlet,命名為portrait
添加Landscape View的Outlet,命名為landscape
5)建立button的Outlet Collection
和以往的略微有些不同,由於我們有兩個View,但是這兩個View中的按鈕的作用是一樣的,所以我們在建立按鈕的Outlet時,可以使用Outlet集合,也就是Outlet Collection,Outlet Collection和Outlet的區別是,Outlet只能對應一個控制項,Outlet Collection則可以對應多個控制項,其實Outlet Collection就是一個Outlet的數組,裡面可以存放任意多個Outlet,然後對其一一進行遍曆。有了Outlet Collection後,我們在寫Action的時候,只需要便利Outlet Collection,就可以其中包含的每個控制項進行操作,會方便很多(否則你需要對每個控制項聲明一個Outlet,然後一一操作,這個不僅增加代碼的複雜度,而且還很容易遺漏控制項)。
添加Outlet Collection的方法和添加一般的Outlet方法一樣,選中Portrait View中的button Foo,按住control鍵,滑鼠拖動到BIDViewController.h,釋放滑鼠,在填出的框中改變類型Connection的類型,改成“Outlet Collection”,並命名為foos,單擊Connect完成添加。
添加完成後,切換到Landscape View,選中Foo按鈕,control + 滑鼠拖動到已添加的Outlet Collection foos上,這樣Landscape View中Foo按鈕也加入到了foos集合中。
使用同樣的方法為兩個Bar按鈕添加Outlet Collection,並命名為bars。完成後的BIDViewController.h檔案如下
#import <UIKit/UIKit.h>@interface BIDViewController : UIViewController@property (strong, nonatomic) IBOutlet UIView *portrait;@property (strong, nonatomic) IBOutlet UIView *landscape;@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *foos;@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *bars;@end
6)添加Action
為4個按鈕添加Action buttonTapped,只要添加一個Action,其他幾個按鈕串連到這個Action即可,完整的BIDViewController.h檔案如下
@interface BIDViewController : UIViewController@property (strong, nonatomic) IBOutlet UIView *portrait;@property (strong, nonatomic) IBOutlet UIView *landscape;@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *foos;@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *bars;- (IBAction)buttonTaped:(id)sender;@end
7)實現View的切換
首先開啟BIDViewController.m檔案,然後添加一個宏定義在最上面(#import的下面)
#define degreesToRadians(x) (M_PI * (x) / 180.0)
這段宏的意思是將角度轉成弧度,在iphone旋轉時會用到,因為iphone的旋轉角度是根據弧度來計算的,並不是角度,因此我們需要進行一個簡單的轉換。M_PI是一個預定義的值,就是3.14159265358979323846264338327950288
重載willAnimateRotationToInterfaceOrientation方法,添加在最後一個@synthesize的後面,如下
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { if(toInterfaceOrientation == UIInterfaceOrientationPortrait) { self.view = self.portrait; self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(0)); self.view.bounds = CGRectMake(0.0, 0.0, 320.0, 460.0); } else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { self.view = self.landscape; self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(-90)); self.view.bounds = CGRectMake(0.0, 0.0, 480.0, 300.0); } else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { self.view = self.landscape; self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(90)); self.view.bounds = CGRectMake(0.0, 0.0, 480.0, 300.0); }}
willAnimateRotationToInterfaceOrientation方法發生在旋轉開始之後但是還未真正旋轉之前,即旋轉這個命令已經發出了,但是還沒有開始旋轉這個動作。
上面的這段code,有幾個地方需要說明一下,我們拿一個if語句塊進行說明
self.view = self.landscape;
根據iphone的選擇方向,選擇顯示哪個View
self.view.transform = CGAffineTransformIdentity;
貌似是將view的旋轉狀態設定到預設狀態,即初始化一下,這個不太瞭解,網上查到的說法是:線性代數裡面講的矩陣變換,這個是恒等變換
當 你改變一個view.transform屬性的時候需要先恢複預設狀態,然後再進行改變。
self.view.transform = CGAffineTransformMakeRotation(degreesToRadians(-90));
view的旋轉弧度,將角度換算成弧度,然後進行旋轉。
self.view.bounds = CGRectMake(0.0, 0.0, 480.0, 300.0);
CGRectMake在上一篇已經講解過,即設定起始點和大小,bounds屬性是第一次遇到,它和frame有些類似,但是不同的是,frame控制項相對於父視圖的位置,而bounds則是控制項自身的位置,即沒有相對於父視圖的概念,因為我們旋轉的都是view,因此其起始點自然都是(0.0 , 0.0)。
上面的這個旋轉方法可以當做模板來使用,每當遇到切換view的時候,就可以直接複製粘貼該方法。
(額外說明一個問題,仔細觀察上面的這個方法中CGRectMake中最後一個參數,最後一個參數是表面view的高度,但是是不是發現它少了20?原因是狀態列,iphone頂部的狀態列的高度是20,因此view的高度會減少20。)
8)實現buttonTappedAction
在BIDViewController.m中找到buttonTapped,添加代碼如下
- (IBAction)buttonTaped:(id)sender { NSString *message = nil; if([self.foos containsObject:sender]) message = @"Foo button pressed"; else message = @"Bar button pressed"; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:message message:nil delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil]; [alert show];}
這段代碼需要注意的就只有這一行
if([self.foos containsObject:sender])
foos是Outlet Collection對象,是一個NSArray,裡面有一個containObject方法,查看是否存在某個對象,上面的if語句的意思就是判斷foos中是否包含觸發buttonTappedAction的對象,即判斷該Action是不是由2個Foo按鈕觸發的,如果不是,那麼即使2個Bar按鈕觸發的。
9)編譯運行
點擊Foo,一個警告框彈出,告訴你Foo按鈕被點擊了
旋轉iphone,點擊Bar,同樣警告框填出,告訴你Bar按鈕被點擊了
Swap 1
10)更新buttonTapped
在開頭的時候,我們說過,因為是2個View進行切換,因此在一個View中發生的變化也要體現在另一個View中,我們在這裡舉一個例子,當在一個View中點擊一個按鈕的時候,隱藏該按鈕,那麼在另一個View中也要把對應的按鈕隱藏,將buttonTapped方法改成如下樣子
- (IBAction)buttonTaped:(id)sender { /* NSString *message = nil; if([self.foos containsObject:sender]) message = @"Foo button pressed"; else message = @"Bar button pressed"; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:message message:nil delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil]; [alert show]; */ if([self.foos containsObject:sender]) { for (UIButton *oneFoo in foos) { oneFoo.hidden = YES; } } else { for (UIButton *oneBar in bars) { oneBar.hidden = YES; } } }
這裡的for語句和C#中的foreach一樣,都是遍曆某個集合中的對象,首先判斷是那個按鈕觸發了buttonTapped,然後就將該按鈕所在的Outlet Collection中的所有對象隱藏,這樣當然也就隱藏了另一個View中的按鈕。
編譯運行,點擊bar按鈕,bar按鈕被隱藏
旋轉iphone,另一個View中的bar按鈕也被隱藏了
Swap All