UIView詳解

weixin_34393428發表於2017-11-21

概述

檢視是應用程式中使用者介面的基本組成部分,UIView類定義了所有檢視的通用行為。檢視在其邊界矩形內呈現內容,並處理與該內容有關的任何互動。UIView類是一個具體類,可以使用其例項物件來顯示一個固定的背景顏色,也可以子類化UIView來繪製更復雜的內容。要展示應用程式中常見的標籤、影象、 按鈕和其它介面元素,應首先選擇使用UIKit框架提供的檢視子類。

檢視物件是應用程式與使用者互動的主要方式,其主要職責有:

  • 繪製圖形和執行動畫
  • 使用UIKit框架或者Core Graphics框架在檢視的矩形區域中繪製內容。
  • 檢視的一些屬性值可以用來執行動畫。
  • 佈局和管理子檢視
  • 檢視可能包含多個子檢視。
  • 檢視可以調整子檢視的大小和位置。
  • 使用Auto Layout定義調整檢視大小和重新定位檢視的規則,並以此規則來響應檢視層的更改。
  • 事件處理
  • 檢視物件是UIResponder類的子類物件,能夠響應觸控事件和其它型別的事件。
  • 檢視可以附加手勢識別器來處理常見的手勢。

建立和管理檢視層次結構

管理檢視層次結構是開發應用程式使用者介面的關鍵部分,檢視層次結構會影響應用程式使用者介面的外觀以及應用程式如何響應更改和事件。下圖顯示了時鐘應用程式的檢視層次結構,標籤欄和導航檢視是標籤欄和導航檢視控制器物件提供的特殊檢視層次結構,用於管理整個使用者介面的各個部分。

4906302-4e3076f5e1efd0f3.jpg
圖2-1

新增和移除檢視

Interface Builder是建立檢視層次結構最便捷的方式,因為我們可以使用圖形方式來組裝檢視,檢視檢視之間的關係,並確切瞭解在執行時將如何顯示這些檢視。如果以程式設計方式建立檢視,可以使用以下方法來排列檢視層次結構:

  • 要將子檢視新增到父檢視,請呼叫父檢視的addSubview:方法,此方法將子檢視新增到父級子檢視層的最上層。
  • 要在父檢視和子檢視中間插入子檢視,請呼叫父檢視的任一insertSubview:...方法,此方法會將子檢視插入到父檢視和給定子檢視之間的檢視層的最上層。
  • 要對父檢視中的現有子檢視進行重新排列,請呼叫父檢視的bringSubviewToFront:sendSubviewToBack:或者exchangeSubviewAtIndex:withSubviewAtIndex:方法,使用這些方法比刪除子檢視並重新插入它們效率要快。
  • 要從父檢視移除子檢視,請呼叫子檢視removeFromSuperview方法。

子檢視的frame屬性值決定了檢視在其父檢視座標系中的原點和尺寸,bounds屬性值決定了檢視的內部尺寸。預設情況下,當子檢視的可見區域超出其父檢視的矩形區域時,不會對子檢視內容作裁剪,但可以設定父檢視物件的clipsToBounds屬性值來更改預設行為。

可以在檢視控制器的loadView或者viewDidLoad方法中新增子檢視到當前檢視層。如果是以程式設計方式建立檢視,則在檢視控制器的loadView方法中建立新增檢視。無論是以程式設計方式建立檢視還是從nib檔案中載入檢視,都可以放在檢視控制器的viewDidLoad方法中執行。

將子檢視新增到另一個檢視時,UIKit會通知父檢視和子檢視。在實現自定義檢視時,可以通過覆寫willMoveToSuperview:willMoveToWindow:willRemoveSubview:didAddSubview:didMoveToSuperview或者didMoveToWindow方法中一個或者多個來攔截這些通知。可以使用這些通知來更新與檢視層次結構相關的任何狀態資訊或者執行其它任務。

隱藏檢視

要以視覺化方式隱藏檢視,可以將檢視的hidden屬性值設為YES或者將其alpha屬性值設為0.0。被隱藏的檢視不會從系統接收到觸控事件,但是可以參與與檢視層次結構相關的自動調整和其它佈局操作。如果想要動畫隱藏或呈現檢視,必須使用檢視的alpha屬性,hidden屬性不支援動畫。

在檢視層中定位檢視

在檢視層中定位檢視有2種方法:

  • 在適當位置儲存檢視物件的指標,例如在擁有此檢視的檢視控制器中。
  • 為每個檢視的tag屬性分配一個唯一的整數,並呼叫其父檢視或者其父檢視的更下層父檢視的viewWithTag:方法來定位它。

viewWithTag:方法會從呼叫該方法的檢視的檢視分支遍歷檢視獲取對應tag值的檢視,在使用該方法定位檢視時,呼叫其父檢視的viewWithTag:方法比呼叫其父檢視的更下層父檢視的viewWithTag:方法的效率要快。

平移、 縮放和旋轉檢視

每個檢視物件都關聯有一個transform仿射變換屬性,可以通過配置transform屬性值來平移、 縮放和旋轉檢視的內容。UIViewtransform屬性包含一個CGAffineTransform結構體,預設情況下,不會修改檢視的外觀。我們可以隨時分配一個新的轉換,例如將檢視旋轉45度,可以使用以下程式碼:

CGAffineTransform xform = CGAffineTransformMakeRotation(M_PI/4.0);

self.view.transform = xform;

將多個轉換同時應用於檢視時,將這些轉換新增到CGAffineTransform結構體的順序非常重要。先旋轉檢視然後平移檢視與先平移檢視然後旋轉檢視的最終效果是不一樣的,即使在每種情況下旋轉和平移的數值都是一樣的。此外,任何轉換都是相對於檢視的中心點而變換的。縮放和旋轉檢視時,不會改變檢視的中心點。有關建立和使用仿射變換的更多資訊,可以參看Quartz 2D Programming Guide中的Transforms.

座標轉換

在某些情況下,特別是在處理觸控事件時,應用程式可能需要將檢視的座標參考系從一個檢視轉移到另一個檢視。例如觸控事件會報告每次觸控在window座標系中位置,但通常我們只需要檢視在其所在檢視層分支中的檢視座標系中的位置。UIView類定義了以下轉換檢視座標參考系的方法:

  • convertPoint:fromView:
  • convertRect:fromView:
  • convertPoint:toView:
  • convertRect:toView:

convert...:fromView:方法將座標點的座標參考系從給定檢視的座標系轉換為呼叫此方法的檢視的區域性座標系,而convert...:toView:則將座標點的座標參考系從呼叫此方法的檢視的區域性座標系轉換為給定檢視的座標系。如果這兩類方法的給定參考檢視為nil,則會自動指定參考檢視為當前檢視所在的window

UIWindow也定義了幾種轉換座標參考系的方法:

  • convertPoint:fromWindow:
  • convertRect:fromWindow:
  • convertPoint:toWindow:
  • convertRect:toWindow:

在被旋轉過的檢視中轉換座標時,UIKit會假定一個大小剛好包含此被旋轉過檢視的螢幕區域為座標點的座標參考系,如下圖所示:

4906302-7cc910f286128079.jpg
圖2-2

在執行時調整檢視的大小和位置

每當檢視的大小發生變化時,其子檢視的大小和位置都必須相應地改變。UIView類支援檢視層中的檢視自動和手動佈局。通過自動佈局,我們可以設定每個檢視在其父檢視調整大小時應遵循的佈局規則,使其可以自動調整大小和位置。通過手動佈局,我們可以根據需要手動調整檢視的大小和位置。

佈局更改

檢視發生以下任何更改時,可能會使檢視的佈局發生更改:

  • 檢視邊界矩形的大小發生變化。
  • 螢幕方向的變換,通常會使根檢視的邊界矩形發生更改。
  • 與檢視的圖層相關聯的核心動畫子圖層組發生更改,並且需要佈局。
  • 呼叫檢視的setNeedsLayout或者layoutIfNeeded方法來強制執行佈局。
  • 呼叫檢視圖層的setNeedsLayout方法來強制佈局。

自動調整檢視佈局

當檢視的大小發生更改時,通常需要更改其子檢視的位置和大小以適配其父檢視的大小。父檢視的autoresizesSubviews屬性決定子檢視是否調整大小,如果此屬性值為YES,則該父檢視會根據其子檢視的autoresizingMask屬性來確定如何調整和定位該子檢視。對任何子檢視的大小進行更改也會觸發子檢視的子檢視的佈局調整。

對於檢視層中的每個檢視,要使其支援自動佈局,就必須將其autoresizingMask屬性設定為合適的值。下表列出了可應用於檢視的自動調整佈局選項,並描述了其在佈局操作期間所起的效果。為autoresizingMask屬性分配值時,可以使用OR運算子組合這些常量,或者將這些常量相加後再賦值。

Autoresizing mask 描述
UIViewAutoresizingNone 檢視不會自動調整大小(預設值)
UIViewAutoresizingFlexibleHeight 根據需要調整檢視的高度,以保證上邊距和下邊距不變。
UIViewAutoresizingFlexibleWidth 根據需要調整檢視的寬度,以保證左邊距和右邊距不變。
UIViewAutoresizingFlexibleLeftMargin 檢視左邊距根據需要增大或減小,以保證檢視右邊距不變。
UIViewAutoresizingFlexibleRightMargin 檢視右邊距根據需要增大或減小,以保證檢視左邊距不變。
UIViewAutoresizingFlexibleBottomMargin 檢視下邊距根據需要增大或減小,以保證檢視上邊距不變。
UIViewAutoresizingFlexibleTopMargin 檢視上邊距根據需要增大或減小,以保證檢視下邊距不變。
4906302-18f601bb55fc0c6c.jpg
圖3-1

手動調整檢視佈局

當檢視的大小更改時,UIKit就會應用其子檢視的自動調整行為,之後會呼叫檢視的layoutSubviews方法。當自定義檢視的子檢視的自動調整行為不能滿足我們的需要時,可以實現該自定義檢視的layoutSubviews方法並在其中執行以下任何操作:

  • 調整任何子檢視的大小和位置。
  • 新增或刪除子檢視或者核心動畫圖層。
  • 通過呼叫子檢視的setNeedsDisplay或者setNeedsDisplayInRect:方法強制其執行重繪。
  • 在實現一個大的可滾動區域時,經常需要手動佈局子檢視。由於直接用一個足夠大的檢視來呈現可滾動內容是不切實際的,所以應用程式通常會實現一個根檢視,其中包含許多較小的檢視塊。每個小檢視塊代表可滾動內容的一部分。當滾動事件發生時,根檢視呼叫其setNeedsLayout方法來執行重繪,之後呼叫layoutSubviews方法並在該方法中根據發生的滾動量重新定位平鋪小檢視塊。

與核心動畫圖層進行互動

每個檢視物件都擁有一個用於管理螢幕上檢視內容的顯示和動畫的核心動畫圖層。雖然我們可以使用檢視物件做很多事情,但也可以根據需要直接使用其圖層物件。檢視的圖層物件儲存在檢視的layer屬性中。

更改與檢視關聯的圖層物件的所屬型別

與檢視關聯的圖層物件所屬型別在建立檢視之後就無法被更改了,所以檢視使用layerClass類方法來指定其圖層物件的所屬類。此方法的預設實現返回CALayer類,更改此方法返回值的唯一方法就是子類化UIView並重寫該方法返回一個不同的類。例如,如果使用平鋪來顯示大的可滾動區域,則可能需要使用CATiledLayer類來支援檢視,程式碼如下:

+ (Class)layerClass
{
    return [CATiledLayer class];
}

檢視會在其初始化前先呼叫其layerClass類方法,並使用返回的類來建立其圖層物件。另外,檢視總是將自己指定為其圖層物件的委託物件。檢視持有圖層,檢視和圖層之間的關係不能改變,也不能在指定該檢視為另一個圖層物件的委託物件。否則,會導致繪製圖形時出問題,應用程式有可能崩潰。

有關Core Animation提供的不同型別的圖層物件的更多資訊,可以參看Core Animation Reference Collection

在檢視中嵌入圖層物件

如果要使用圖層物件而不用檢視,則可以根據需要將自定義圖層物件新增到圖層中。自定義圖層物件是屬於CALayer類的任何例項,通常以程式設計方式來建立自定義圖層,並使用Core Animation的規則將其合併。自定義圖層不會接收到事件,也不會參與響應者鏈,但能根據Core Animation的規則繪製自己的圖形並響應其父檢視或父圖層中的大小更改。

使用自定義圖層物件顯示靜態圖片的程式碼如下:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Create the layer.
    CALayer* myLayer = [[CALayer alloc] init];

    // Set the contents of the layer to a fixed image. And set
    // the size of the layer to match the image size.
    UIImage layerContents = [[UIImage imageNamed:@"myImage"] retain];
    CGSize imageSize = layerContents.size;

    myLayer.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height);
    myLayer = layerContents.CGImage;

    // Add the layer to the view.
    CALayer*    viewLayer = self.view.layer;
    [viewLayer addSublayer:myLayer];

    // Center the layer in the view.
    CGRect        viewBounds = backingView.bounds;
    myLayer.position = CGPointMake(CGRectGetMidX(viewBounds), CGRectGetMidY(viewBounds));

    // Release the layer, since it is retained by the view's layer
    [myLayer release];
}

可以新增任意數量的子圖層到檢視的圖層,有關如何直接使用圖層的資訊,可以參看Core Animation Programming Guide

檢視繪圖週期

當首次顯示檢視時,或者由於佈局更改而全部或部分檢視變為可見時,系統會呼叫檢視的drawRect:方法來繪製其內容。可以在此方法中將檢視的內容繪製到當前圖形上下文中,該圖形上下文在呼叫此方法之前由系統自動設定。注意,系統每次設定當前圖形上下文可能並不相同,所以在每次繪製時,需要使用UIGraphicsGetCurrentContext方法來重新獲取當前圖形上下文。

當檢視的實際內容發生變化時,需要呼叫檢視的setNeedsDisplay或者setNeedsDisplayInRect:方法來通知系統當前檢視需要重新繪製。這些方法會標記當前檢視需要更新,系統會在下一個繪圖週期中更新檢視。由於在呼叫這些方法後,系統會等到下一個繪圖週期才更新檢視,所以可以在多個檢視中呼叫這些方法來同時更新它們。

注意:如果使用OpenGL ES來執行繪圖,則應使用GLKView類,有關如何使用OpenGL ES進行繪製的更多資訊,可以參看OpenGL ES Programming Guide

動畫更改檢視的屬性

檢視的frameboundscentertransformalphabackgroundColor屬性是可以用來執行動畫。

使用基於Block的方法執行動畫

iOS 4 以後,可以使用使用基於Block的方法來執行動畫。有以下幾種基於Block的方法為動畫塊提供不同級別的配置:

  • animateWithDuration:animations:
  • animateWithDuration:animations:completion:
  • animateWithDuration:delay:options:animations:completion

這些方法都是類方法,使用它們建立的動畫塊不會繫結到單個檢視。因此,可以使用這些方法建立一個包含對多個檢視進行更改的動畫。例如,在某個時間段淡入淡出執行檢視顯示和隱藏動畫。其程式碼如下:

[UIView animateWithDuration:1.0 animations:^{

    firstView.alpha = 0.0;
    secondView.alpha = 1.0;
}];

程式執行以上程式碼時,會在另一個執行緒執行指定的動畫,以避免阻塞當前執行緒或應用程式的主執行緒。

如果要更改預設的動畫引數,則必須使用animateWithDuration:delay:options:animations:completion方法來執行動畫。可以通過該方法來自定義以下動畫引數:

  • 延遲開始執行的動畫的時間
  • 動畫時使用的時間曲線的型別
  • 動畫重複執行的次數
  • 當動畫執行到最後時,動畫是否自動反轉
  • 在動畫執行過程中,檢視是否能接收觸控事件
  • 動畫是否應該中斷任何正在執行的動畫,或者等到正在執行的動畫完成之後才開始

另外,animateWithDuration:animations:completion:animateWithDuration:delay:options:animations:completion方法可以指定動畫完成後的執行程式碼塊,可以在塊中將單獨的動畫連結起來。例如,第一次呼叫animateWithDuration:delay:options:animations:completion方法設定一個淡出動畫,並配置一些動畫引數。當動畫完成後,在動畫完成後的執行程式碼塊中延遲執行淡入動畫。其程式碼如下:

// Fade out the view right away
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{

    thirdView.alpha = 0.0;

}completion:^(BOOL finished){

    // Wait one second and then fade in the view
    [UIView animateWithDuration:1.0 delay:1.0 options:UIViewAnimationOptionCurveEaseOut
    animations:^{

        thirdView.alpha = 1.0;

    }completion:nil];
}];

重要:當正在對檢視的某個屬性執行動畫時,此時更改該屬性值不會停止當前動畫。相反,當前動畫會繼續執行,並動畫到剛分配給該屬性的新值。

使用Begin/Commit方法執行動畫

iOS 4 之前,只能使用UIView類的beginAnimations:context:commitAnimations類方法來定義動畫塊,這兩個方法用來標記動畫塊的開始和結束。在呼叫commitAnimations方法之後,在這兩個方法之間更改的任何動畫屬性都會動畫過渡到其新值。動畫會在輔助執行緒上執行,以避免阻塞當前執行緒或應用程式的主執行緒。

使用Begin/Commit方法在某個時間段淡入淡出執行檢視顯示和隱藏動畫。其程式碼如下:

[UIView beginAnimations:@"ToggleViews" context:nil];
[UIView setAnimationDuration:1.0];

// Make the animatable changes.
firstView.alpha = 0.0;
secondView.alpha = 1.0;

// Commit the changes and perform the animation.
[UIView commitAnimations];

預設情況下,在動畫塊中的所有動畫屬性更改都是動畫過渡的。如果想讓某些屬性更改不支援動畫過渡,可以先呼叫setAnimationsEnabled:來臨時禁用動畫,然後執行不需要動畫過渡的更改。之後,再次呼叫setAnimationsEnabled:方法重新啟用動畫。可以通過呼叫areAnimationsEnabled類方法來判斷動畫是否被啟用。

注意:當正在對檢視的某個屬性執行動畫時,此時更改該屬性值不會停止當前動畫。相反,當前動畫會繼續執行,並動畫到剛分配給該屬性的新值。

可以使用以下類方法為Begin/Commit動畫塊配置動畫引數:

  • setAnimationStartDate::設定開始執行動畫的時間。如果設定的日期是過去時間,則會立即執行動畫。
  • setAnimationDelay::設定當前時間延遲多少秒後開始執行動畫。
  • setAnimationDuration::設定動畫時長。
  • setAnimationCurve::設定動畫時使用的時間曲線的型別。
  • setAnimationRepeatCount::設定動畫重複次數。
  • setAnimationRepeatAutoreverses::設定動畫完成後是否自動反轉。

如果想要在動畫開始前或完成後執行某些操作,則必須將委託物件和操作方法與動畫塊關聯起來。使用UIView類的setAnimationDelegate:類方法設定委託物件,並使用setAnimationWillStartSelector:setAnimationDidStopSelector:類方法來設定動畫開始前和完成後要執行的方法。系統會在適當的時候呼叫委託方法,讓我們有機會執行需要執行的程式碼。

動畫委託方法的方法名類似於一下:

- (void)animationWillStart:(NSString *)animationID context:(void *)context;

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;

這兩個方法的animationIDcontext引數與呼叫beginAnimations:context:方法開啟動畫時傳入的引數相同:

  • animationID——用於識別動畫的字串。
  • context——使用該上下文物件傳遞資訊給委託物件。

呼叫setAnimationDidStopSelector:類方法關聯的動畫停止時執行的方法有一個額外的finished引數,其是一個布林值。如果動畫執行完成,為YES。如果動畫被其他動畫提前取消或停止,則為NO

巢狀動畫塊

可以通過在動畫塊內巢狀其他動畫塊來為動畫塊的某些部分分配不同的時序和配置選項。巢狀動畫會與任何父動畫同時啟動,但根據它們各自的配置引數來執行。預設情況下,巢狀動畫會繼承父級動畫的持續時間和動畫曲線,但可以根據需要重置巢狀動畫的這些選項。

以下程式碼展示了一個如何使用巢狀動畫塊來改變動畫組中的某些動畫的開啟時間,持續時間和行為的例子。有兩個檢視正在被淡化為完全透明,但其中一個檢視的透明度會在動畫結束前來回多次改變。在巢狀動畫塊中配置的UIViewAnimationOptionOverrideInheritedCurveUIViewAnimationOptionOverrideInheritedDuration引數將允許巢狀動畫使用自己的動畫曲線和持續時間值。如果沒有配置這些引數,巢狀動畫將使用父級動畫塊的動畫曲線和持續時間。

[UIView animateWithDuration:1.0 delay:1.0 options:UIViewAnimationOptionCurveEaseOut
animations:^{

    aView.alpha = 0.0;

    // Create a nested animation that has a different
    // duration, timing curve, and configuration.
    [UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionOverrideInheritedCurve |
    UIViewAnimationOptionCurveLinear | UIViewAnimationOptionOverrideInheritedDuration |
    UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse
    animations:^{

        [UIView setAnimationRepeatCount:2.5];

        anotherView.alpha = 0.0;
    }completion:nil];
    
}completion:nil];

如果是使用Begin/Commit方法建立動畫,其巢狀使用與基於Block的方法類似。在已經建立的動畫塊中呼叫beginAnimations:context:方法繼續建立一個新的動畫塊,並根據需要進行配置。任何配置更改都適用於最新建立的動畫塊。在提交和執行動畫前,所有巢狀動畫塊都必須呼叫commitAnimations方法提交動畫。

反轉動畫

建立可重複執行的可反轉動畫時,要注意將重複次數指定為非整數值。對於可反轉動畫,每個動畫週期內都包含從原始值到新值,然後再還原為原始值的動畫。如果希望動畫在新值上結束,則重複執行次數要加0.5以增加半個額外動畫週期。

檢視過渡轉換動畫

檢視過渡轉換可以隱藏在檢視層中新增、刪除或顯示檢視帶來的視覺上的突然變化。可以使用檢視過渡轉換來實現以下型別的更改:

  • 更改現有檢視的可見子檢視,使父檢視在不同狀態之間切換。
  • 當想使介面有很大的改變時,使用不同的檢視替換檢視層中的某個檢視。

注意:不要將檢視轉換與檢視控制器的跳轉相混淆,檢視轉換僅影響檢視層。

更改檢視的子檢視

在iOS 4之後,使用transitionWithView:duration:options:animations:completion:方法為檢視啟動過渡動畫。通常情況下,在此方法指定的動畫塊中,應執行與顯示、隱藏、新增或者刪除子檢視相關的動畫。這樣就能允許檢視物件建立檢視在更改之前和更改之後的截圖,並且會在這兩張截圖之間建立動畫。這種方式更加高效,但是,如果還需要對其他更改執行動畫,則可以在呼叫此方法時配置UIViewAnimationOptionAllowAnimatedContent選項,這樣就可以防止檢視物件建立截圖,並直接對所有更改執行動畫。

以下程式碼是如何使用過渡轉換動畫使使用者介面看起來好像新增了新的文字輸入頁面的示例。使用者介面包含兩個嵌入的文字檢視,文字檢視的配置相同,但其中一個始終可見,另一個隱藏。當使用者點選按鈕建立一個新頁面時,這個方法切換了兩個檢視的可見性,導致一個空的文字檢視的新空白頁面準備接收文字。轉換完成後,檢視使用私有方法儲存舊頁面中的文字,並重置現在隱藏的文字檢視,以便稍後重新使用。

- (IBAction)displayNewPage:(id)sender
{
    [UIView transitionWithView:self.view duration:1.0 options:UIViewAnimationOptionTransitionCurlUp
    animations:^{

        currentTextView.hidden = YES;
        swapTextView.hidden = NO;

    }completion:^(BOOL finished){

        // Save the old text and then swap the views.
        [self saveNotes:temp];

        UIView*    temp = currentTextView;
        currentTextView = swapTextView;
        swapTextView = temp;
    }];
}

iOS 4之前的版本可以使用setAnimationTransition:forView:cache:方法執行檢視轉換動畫,程式碼如下:

[UIView beginAnimations:@"ToggleSiblings" context:nil];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
[UIView setAnimationDuration:1.0];

// Make your changes


[UIView commitAnimations];

另外,還需要設定好委託物件和動畫停止後執行的回撥方法。

用不同的檢視替換檢視層中的某個檢視

iOS 4之後,使用transitionFromView:toView:duration:options:completion:方法在兩個檢視間過渡轉換。此方法實際上是從當前檢視層中刪除第一個檢視,然後插入另一個檢視。如果要隱藏檢視而不是從檢視層中刪除檢視,可以在呼叫此方法時配置UIViewAnimationOptionShowHideTransitionViews選項。

以下程式碼展示瞭如何在單個檢視控制器管理的兩個主檢視之間交換顯示。檢視控制器的根檢視總是顯示兩個子檢視中的一個,每個檢視呈現的內容相同,但介面佈局不同。檢視控制器使用displayingPrimary成員變數(布林值)來跟蹤在任何給定時間顯示哪個檢視。翻轉方向根據正在顯示的檢視而改變。

- (IBAction)toggleMainViews:(id)sender {

    [UIView transitionFromView:(displayingPrimary ? primaryView : secondaryView) toView:(displayingPrimary ? secondaryView : primaryView) duration:1.0 options:(displayingPrimary UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft) completion:^(BOOL finished) {

        if (finished)
        {
            displayingPrimary = !displayingPrimary;
        }
    }];
}

注意:除了交換檢視之外,還需要在檢視控制器中執行程式碼來管理主檢視和輔助檢視的載入和解除安裝。有關如何通過檢視控制器載入和解除安裝檢視的資訊,可以參看View Controller Programming Guide for iOS

檢視和檢視的圖層一起動畫更改

應用程式可以根據需要自由地混合基於檢視和基於圖層的動畫程式碼,但配置動畫引數的過程取決於誰擁有圖層。更改檢視擁有的圖層與更改檢視本身相同,並且應用於圖層屬性的任何動畫都根據當前基於檢視的動畫塊的動畫引數來執行。自定義圖層物件會忽略基於檢視的動畫塊引數,而是使用預設的Core Animation引數。

如果要為所建立的圖層自定義動畫引數,則必須直接使用Core Animation。通常,使用Core Animation動畫化圖層需要建立一個CABasicAnimation物件或者CAAnimation的其他子類物件,然後將該動畫物件新增到相應的圖層。

以下程式碼實現了一個動畫,其同時修改一個檢視和一個自定義圖層。檢視在其邊界的中心包含一個自定義CALayer物件。動畫順時針旋轉檢視,同時逆時針旋轉圖層。由於旋轉方向相反,圖層相對於螢幕保持其原始角度,看上去並沒有旋轉。

[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveLinear
animations:^{

    // Animate the first half of the view rotation.
    CGAffineTransform  xform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-180));
    backingView.transform = xform;

    // Rotate the embedded CALayer in the opposite direction.
    CABasicAnimation*    layerAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
    layerAnimation.duration = 2.0;
    layerAnimation.beginTime = 0; //CACurrentMediaTime() + 1;
    layerAnimation.valueFunction = [CAValueFunction functionWithName:kCAValueFunctionRotateZ];
    layerAnimation.timingFunction = [CAMediaTimingFunction
    functionWithName:kCAMediaTimingFunctionLinear];
    layerAnimation.fromValue = [NSNumber numberWithFloat:0.0];
    layerAnimation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(360.0)];
    layerAnimation.byValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(180.0)];
    [manLayer addAnimation:layerAnimation forKey:@"layerAnimation"];

}completion:^(BOOL finished){
    // Now do the second half of the view rotation.
    [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveLinear
    animations:^{

        CGAffineTransform  xform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-359));
        backingView.transform = xform;

    }completion:^(BOOL finished){

        backingView.transform = CGAffineTransformIdentity;
    }];
}];

注意:也可以在基於檢視的動畫塊之外建立並應用CABasicAnimation物件,以獲得相同的結果。所有的動畫最終都依靠Core Animation來執行。因此,如果動畫幾乎被同時提交,它們就會一起執行。

其他

對應用程式使用者介面的操作必須在主執行緒上執行,也就是說必須在主執行緒中執行UIView類的方法。建立檢視物件不一定要放在主執行緒,但其他所有操作都應該在主執行緒上進行。

自定義列印輸出檢視資訊,可以實現drawRect:forViewPrintFormatter:方法。有關如何支援列印輸出檢視的詳細資訊,可以參看Drawing and Printing Guide for iOS

Demo

示例程式碼下載地址:https://github.com/zhangshijian/UIViewDemo

相關文章