UIView詳解

概述

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

視圖對象是應用程序與用戶交互的主要方式,其主要職責有:

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

創建和管理視圖層次結構

管理視圖層次結構是開發應用程序用戶界面的關鍵部分,視圖層次結構會影響應用程序用戶界面的外觀以及應用程序如何響應更改和事件。下圖顯示了時鐘應用程序的視圖層次結構,標簽欄和導航視圖是標簽欄和導航視圖控制器對象提供的特殊視圖層次結構,用于管理整個用戶界面的各個部分。

圖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會假定一個大小剛好包含此被旋轉過視圖的屏幕區域為坐標點的坐標參考系,如下圖所示:

圖2-2

在運行時調整視圖的大小和位置

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

布局更改

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

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

自動調整視圖布局

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

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

Autoresizing mask 描述
UIViewAutoresizingNone 視圖不會自動調整大小(默認值)
UIViewAutoresizingFlexibleHeight 根據需要調整視圖的高度,以保證上邊距和下邊距不變。
UIViewAutoresizingFlexibleWidth 根據需要調整視圖的寬度,以保證左邊距和右邊距不變。
UIViewAutoresizingFlexibleLeftMargin 視圖左邊距根據需要增大或減小,以保證視圖右邊距不變。
UIViewAutoresizingFlexibleRightMargin 視圖右邊距根據需要增大或減小,以保證視圖左邊距不變。
UIViewAutoresizingFlexibleBottomMargin 視圖下邊距根據需要增大或減小,以保證視圖上邊距不變。
UIViewAutoresizingFlexibleTopMargin 視圖上邊距根據需要增大或減小,以保證視圖下邊距不變。
圖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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容