UIView詳解

概述

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

視圖對象是應(yīng)用程序與用戶交互的主要方式,其主要職責(zé)有:

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

創(chuàng)建和管理視圖層次結(jié)構(gòu)

管理視圖層次結(jié)構(gòu)是開發(fā)應(yīng)用程序用戶界面的關(guān)鍵部分,視圖層次結(jié)構(gòu)會影響應(yīng)用程序用戶界面的外觀以及應(yīng)用程序如何響應(yīng)更改和事件。下圖顯示了時鐘應(yīng)用程序的視圖層次結(jié)構(gòu),標簽欄和導(dǎo)航視圖是標簽欄和導(dǎo)航視圖控制器對象提供的特殊視圖層次結(jié)構(gòu),用于管理整個用戶界面的各個部分。

圖2-1

添加和移除視圖

Interface Builder是創(chuàng)建視圖層次結(jié)構(gòu)最便捷的方式,因為我們可以使用圖形方式來組裝視圖,查看視圖之間的關(guān)系,并確切了解在運行時將如何顯示這些視圖。如果以編程方式創(chuàng)建視圖,可以使用以下方法來排列視圖層次結(jié)構(gòu):

  • 要將子視圖添加到父視圖,請調(diào)用父視圖的addSubview:方法,此方法將子視圖添加到父級子視圖層的最上層。
  • 要在父視圖和子視圖中間插入子視圖,請調(diào)用父視圖的任一insertSubview:...方法,此方法會將子視圖插入到父視圖和給定子視圖之間的視圖層的最上層。
  • 要對父視圖中的現(xiàn)有子視圖進行重新排列,請調(diào)用父視圖的bringSubviewToFront:、sendSubviewToBack:或者exchangeSubviewAtIndex:withSubviewAtIndex:方法,使用這些方法比刪除子視圖并重新插入它們效率要快。
  • 要從父視圖移除子視圖,請調(diào)用子視圖removeFromSuperview方法。

子視圖的frame屬性值決定了視圖在其父視圖坐標系中的原點和尺寸,bounds屬性值決定了視圖的內(nèi)部尺寸。默認情況下,當子視圖的可見區(qū)域超出其父視圖的矩形區(qū)域時,不會對子視圖內(nèi)容作裁剪,但可以設(shè)置父視圖對象的clipsToBounds屬性值來更改默認行為。

可以在視圖控制器的loadView或者viewDidLoad方法中添加子視圖到當前視圖層。如果是以編程方式創(chuàng)建視圖,則在視圖控制器的loadView方法中創(chuàng)建添加視圖。無論是以編程方式創(chuàng)建視圖還是從nib文件中加載視圖,都可以放在視圖控制器的viewDidLoad方法中執(zhí)行。

將子視圖添加到另一個視圖時,UIKit會通知父視圖和子視圖。在實現(xiàn)自定義視圖時,可以通過覆寫willMoveToSuperview:、willMoveToWindow:willRemoveSubview:、didAddSubview:didMoveToSuperview或者didMoveToWindow方法中一個或者多個來攔截這些通知??梢允褂眠@些通知來更新與視圖層次結(jié)構(gòu)相關(guān)的任何狀態(tài)信息或者執(zhí)行其它任務(wù)。

隱藏視圖

要以可視化方式隱藏視圖,可以將視圖的hidden屬性值設(shè)為YES或者將其alpha屬性值設(shè)為0.0。被隱藏的視圖不會從系統(tǒng)接收到觸摸事件,但是可以參與與視圖層次結(jié)構(gòu)相關(guān)的自動調(diào)整和其它布局操作。如果想要動畫隱藏或呈現(xiàn)視圖,必須使用視圖的alpha屬性,hidden屬性不支持動畫。

在視圖層中定位視圖

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

  • 在適當位置存儲視圖對象的指針,例如在擁有此視圖的視圖控制器中。
  • 為每個視圖的tag屬性分配一個唯一的整數(shù),并調(diào)用其父視圖或者其父視圖的更下層父視圖的viewWithTag:方法來定位它。

viewWithTag:方法會從調(diào)用該方法的視圖的視圖分支遍歷視圖獲取對應(yīng)tag值的視圖,在使用該方法定位視圖時,調(diào)用其父視圖的viewWithTag:方法比調(diào)用其父視圖的更下層父視圖的viewWithTag:方法的效率要快。

平移、 縮放和旋轉(zhuǎn)視圖

每個視圖對象都關(guān)聯(lián)有一個transform仿射變換屬性,可以通過配置transform屬性值來平移、 縮放和旋轉(zhuǎn)視圖的內(nèi)容。UIViewtransform屬性包含一個CGAffineTransform結(jié)構(gòu)體,默認情況下,不會修改視圖的外觀。我們可以隨時分配一個新的轉(zhuǎn)換,例如將視圖旋轉(zhuǎn)45度,可以使用以下代碼:

CGAffineTransform xform = CGAffineTransformMakeRotation(M_PI/4.0);

self.view.transform = xform;

將多個轉(zhuǎn)換同時應(yīng)用于視圖時,將這些轉(zhuǎn)換添加到CGAffineTransform結(jié)構(gòu)體的順序非常重要。先旋轉(zhuǎn)視圖然后平移視圖與先平移視圖然后旋轉(zhuǎn)視圖的最終效果是不一樣的,即使在每種情況下旋轉(zhuǎn)和平移的數(shù)值都是一樣的。此外,任何轉(zhuǎn)換都是相對于視圖的中心點而變換的。縮放和旋轉(zhuǎn)視圖時,不會改變視圖的中心點。有關(guān)創(chuàng)建和使用仿射變換的更多信息,可以參看Quartz 2D Programming Guide中的Transforms.

坐標轉(zhuǎn)換

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

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

convert...:fromView:方法將坐標點的坐標參考系從給定視圖的坐標系轉(zhuǎn)換為調(diào)用此方法的視圖的局部坐標系,而convert...:toView:則將坐標點的坐標參考系從調(diào)用此方法的視圖的局部坐標系轉(zhuǎn)換為給定視圖的坐標系。如果這兩類方法的給定參考視圖為nil,則會自動指定參考視圖為當前視圖所在的window。

UIWindow也定義了幾種轉(zhuǎn)換坐標參考系的方法:

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

在被旋轉(zhuǎn)過的視圖中轉(zhuǎn)換坐標時,UIKit會假定一個大小剛好包含此被旋轉(zhuǎn)過視圖的屏幕區(qū)域為坐標點的坐標參考系,如下圖所示:

圖2-2

在運行時調(diào)整視圖的大小和位置

每當視圖的大小發(fā)生變化時,其子視圖的大小和位置都必須相應(yīng)地改變。UIView類支持視圖層中的視圖自動和手動布局。通過自動布局,我們可以設(shè)置每個視圖在其父視圖調(diào)整大小時應(yīng)遵循的布局規(guī)則,使其可以自動調(diào)整大小和位置。通過手動布局,我們可以根據(jù)需要手動調(diào)整視圖的大小和位置。

布局更改

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

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

自動調(diào)整視圖布局

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

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

Autoresizing mask 描述
UIViewAutoresizingNone 視圖不會自動調(diào)整大小(默認值)
UIViewAutoresizingFlexibleHeight 根據(jù)需要調(diào)整視圖的高度,以保證上邊距和下邊距不變。
UIViewAutoresizingFlexibleWidth 根據(jù)需要調(diào)整視圖的寬度,以保證左邊距和右邊距不變。
UIViewAutoresizingFlexibleLeftMargin 視圖左邊距根據(jù)需要增大或減小,以保證視圖右邊距不變。
UIViewAutoresizingFlexibleRightMargin 視圖右邊距根據(jù)需要增大或減小,以保證視圖左邊距不變。
UIViewAutoresizingFlexibleBottomMargin 視圖下邊距根據(jù)需要增大或減小,以保證視圖上邊距不變。
UIViewAutoresizingFlexibleTopMargin 視圖上邊距根據(jù)需要增大或減小,以保證視圖下邊距不變。
圖3-1

手動調(diào)整視圖布局

當視圖的大小更改時,UIKit就會應(yīng)用其子視圖的自動調(diào)整行為,之后會調(diào)用視圖的layoutSubviews方法。當自定義視圖的子視圖的自動調(diào)整行為不能滿足我們的需要時,可以實現(xiàn)該自定義視圖的layoutSubviews方法并在其中執(zhí)行以下任何操作:

  • 調(diào)整任何子視圖的大小和位置。
  • 添加或刪除子視圖或者核心動畫圖層。
  • 通過調(diào)用子視圖的setNeedsDisplay或者setNeedsDisplayInRect:方法強制其執(zhí)行重繪。
  • 在實現(xiàn)一個大的可滾動區(qū)域時,經(jīng)常需要手動布局子視圖。由于直接用一個足夠大的視圖來呈現(xiàn)可滾動內(nèi)容是不切實際的,所以應(yīng)用程序通常會實現(xiàn)一個根視圖,其中包含許多較小的視圖塊。每個小視圖塊代表可滾動內(nèi)容的一部分。當滾動事件發(fā)生時,根視圖調(diào)用其setNeedsLayout方法來執(zhí)行重繪,之后調(diào)用layoutSubviews方法并在該方法中根據(jù)發(fā)生的滾動量重新定位平鋪小視圖塊。

與核心動畫圖層進行交互

每個視圖對象都擁有一個用于管理屏幕上視圖內(nèi)容的顯示和動畫的核心動畫圖層。雖然我們可以使用視圖對象做很多事情,但也可以根據(jù)需要直接使用其圖層對象。視圖的圖層對象存儲在視圖的layer屬性中。

更改與視圖關(guān)聯(lián)的圖層對象的所屬類型

與視圖關(guān)聯(lián)的圖層對象所屬類型在創(chuàng)建視圖之后就無法被更改了,所以視圖使用layerClass類方法來指定其圖層對象的所屬類。此方法的默認實現(xiàn)返回CALayer類,更改此方法返回值的唯一方法就是子類化UIView并重寫該方法返回一個不同的類。例如,如果使用平鋪來顯示大的可滾動區(qū)域,則可能需要使用CATiledLayer類來支持視圖,代碼如下:

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

視圖會在其初始化前先調(diào)用其layerClass類方法,并使用返回的類來創(chuàng)建其圖層對象。另外,視圖總是將自己指定為其圖層對象的委托對象。視圖持有圖層,視圖和圖層之間的關(guān)系不能改變,也不能在指定該視圖為另一個圖層對象的委托對象。否則,會導(dǎo)致繪制圖形時出問題,應(yīng)用程序有可能崩潰。

有關(guān)Core Animation提供的不同類型的圖層對象的更多信息,可以參看Core Animation Reference Collection

在視圖中嵌入圖層對象

如果要使用圖層對象而不用視圖,則可以根據(jù)需要將自定義圖層對象添加到圖層中。自定義圖層對象是屬于CALayer類的任何實例,通常以編程方式來創(chuàng)建自定義圖層,并使用Core Animation的規(guī)則將其合并。自定義圖層不會接收到事件,也不會參與響應(yīng)者鏈,但能根據(jù)Core Animation的規(guī)則繪制自己的圖形并響應(yīng)其父視圖或父圖層中的大小更改。

使用自定義圖層對象顯示靜態(tài)圖片的代碼如下:

- (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];
}

可以添加任意數(shù)量的子圖層到視圖的圖層,有關(guān)如何直接使用圖層的信息,可以參看Core Animation Programming Guide。

視圖繪圖周期

當首次顯示視圖時,或者由于布局更改而全部或部分視圖變?yōu)榭梢姇r,系統(tǒng)會調(diào)用視圖的drawRect:方法來繪制其內(nèi)容??梢栽诖朔椒ㄖ袑⒁晥D的內(nèi)容繪制到當前圖形上下文中,該圖形上下文在調(diào)用此方法之前由系統(tǒng)自動設(shè)置。注意,系統(tǒng)每次設(shè)置當前圖形上下文可能并不相同,所以在每次繪制時,需要使用UIGraphicsGetCurrentContext方法來重新獲取當前圖形上下文。

當視圖的實際內(nèi)容發(fā)生變化時,需要調(diào)用視圖的setNeedsDisplay或者setNeedsDisplayInRect:方法來通知系統(tǒng)當前視圖需要重新繪制。這些方法會標記當前視圖需要更新,系統(tǒng)會在下一個繪圖周期中更新視圖。由于在調(diào)用這些方法后,系統(tǒng)會等到下一個繪圖周期才更新視圖,所以可以在多個視圖中調(diào)用這些方法來同時更新它們。

注意:如果使用OpenGL ES來執(zhí)行繪圖,則應(yīng)使用GLKView類,有關(guān)如何使用OpenGL ES進行繪制的更多信息,可以參看OpenGL ES Programming Guide。

動畫更改視圖的屬性

視圖的frame、boundscenter、transform、alphabackgroundColor屬性是可以用來執(zhí)行動畫。

使用基于Block的方法執(zhí)行動畫

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

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

這些方法都是類方法,使用它們創(chuàng)建的動畫塊不會綁定到單個視圖。因此,可以使用這些方法創(chuàng)建一個包含對多個視圖進行更改的動畫。例如,在某個時間段淡入淡出執(zhí)行視圖顯示和隱藏動畫。其代碼如下:

[UIView animateWithDuration:1.0 animations:^{

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

程序執(zhí)行以上代碼時,會在另一個線程執(zhí)行指定的動畫,以避免阻塞當前線程或應(yīng)用程序的主線程。

如果要更改默認的動畫參數(shù),則必須使用animateWithDuration:delay:options:animations:completion方法來執(zhí)行動畫??梢酝ㄟ^該方法來自定義以下動畫參數(shù):

  • 延遲開始執(zhí)行的動畫的時間
  • 動畫時使用的時間曲線的類型
  • 動畫重復(fù)執(zhí)行的次數(shù)
  • 當動畫執(zhí)行到最后時,動畫是否自動反轉(zhuǎn)
  • 在動畫執(zhí)行過程中,視圖是否能接收觸摸事件
  • 動畫是否應(yīng)該中斷任何正在執(zhí)行的動畫,或者等到正在執(zhí)行的動畫完成之后才開始

另外,animateWithDuration:animations:completion:animateWithDuration:delay:options:animations:completion方法可以指定動畫完成后的執(zhí)行代碼塊,可以在塊中將單獨的動畫鏈接起來。例如,第一次調(diào)用animateWithDuration:delay:options:animations:completion方法設(shè)置一個淡出動畫,并配置一些動畫參數(shù)。當動畫完成后,在動畫完成后的執(zhí)行代碼塊中延遲執(zhí)行淡入動畫。其代碼如下:

// 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];
}];

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

使用Begin/Commit方法執(zhí)行動畫

iOS 4 之前,只能使用UIView類的beginAnimations:context:commitAnimations類方法來定義動畫塊,這兩個方法用來標記動畫塊的開始和結(jié)束。在調(diào)用commitAnimations方法之后,在這兩個方法之間更改的任何動畫屬性都會動畫過渡到其新值。動畫會在輔助線程上執(zhí)行,以避免阻塞當前線程或應(yīng)用程序的主線程。

使用Begin/Commit方法在某個時間段淡入淡出執(zhí)行視圖顯示和隱藏動畫。其代碼如下:

[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];

默認情況下,在動畫塊中的所有動畫屬性更改都是動畫過渡的。如果想讓某些屬性更改不支持動畫過渡,可以先調(diào)用setAnimationsEnabled:來臨時禁用動畫,然后執(zhí)行不需要動畫過渡的更改。之后,再次調(diào)用setAnimationsEnabled:方法重新啟用動畫。可以通過調(diào)用areAnimationsEnabled類方法來判斷動畫是否被啟用。

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

可以使用以下類方法為Begin/Commit動畫塊配置動畫參數(shù):

  • setAnimationStartDate::設(shè)置開始執(zhí)行動畫的時間。如果設(shè)置的日期是過去時間,則會立即執(zhí)行動畫。
  • setAnimationDelay::設(shè)置當前時間延遲多少秒后開始執(zhí)行動畫。
  • setAnimationDuration::設(shè)置動畫時長。
  • setAnimationCurve::設(shè)置動畫時使用的時間曲線的類型。
  • setAnimationRepeatCount::設(shè)置動畫重復(fù)次數(shù)。
  • setAnimationRepeatAutoreverses::設(shè)置動畫完成后是否自動反轉(zhuǎn)。

如果想要在動畫開始前或完成后執(zhí)行某些操作,則必須將委托對象和操作方法與動畫塊關(guān)聯(lián)起來。使用UIView類的setAnimationDelegate:類方法設(shè)置委托對象,并使用setAnimationWillStartSelector:setAnimationDidStopSelector:類方法來設(shè)置動畫開始前和完成后要執(zhí)行的方法。系統(tǒng)會在適當?shù)臅r候調(diào)用委托方法,讓我們有機會執(zhí)行需要執(zhí)行的代碼。

動畫委托方法的方法名類似于一下:

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

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

這兩個方法的animationIDcontext參數(shù)與調(diào)用beginAnimations:context:方法開啟動畫時傳入的參數(shù)相同:

  • animationID——用于識別動畫的字符串。
  • context——使用該上下文對象傳遞信息給委托對象。

調(diào)用setAnimationDidStopSelector:類方法關(guān)聯(lián)的動畫停止時執(zhí)行的方法有一個額外的finished參數(shù),其是一個布爾值。如果動畫運行完成,為YES。如果動畫被其他動畫提前取消或停止,則為NO。

嵌套動畫塊

可以通過在動畫塊內(nèi)嵌套其他動畫塊來為動畫塊的某些部分分配不同的時序和配置選項。嵌套動畫會與任何父動畫同時啟動,但根據(jù)它們各自的配置參數(shù)來執(zhí)行。默認情況下,嵌套動畫會繼承父級動畫的持續(xù)時間和動畫曲線,但可以根據(jù)需要重置嵌套動畫的這些選項。

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

[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方法創(chuàng)建動畫,其嵌套使用與基于Block的方法類似。在已經(jīng)創(chuàng)建的動畫塊中調(diào)用beginAnimations:context:方法繼續(xù)創(chuàng)建一個新的動畫塊,并根據(jù)需要進行配置。任何配置更改都適用于最新創(chuàng)建的動畫塊。在提交和執(zhí)行動畫前,所有嵌套動畫塊都必須調(diào)用commitAnimations方法提交動畫。

反轉(zhuǎn)動畫

創(chuàng)建可重復(fù)執(zhí)行的可反轉(zhuǎn)動畫時,要注意將重復(fù)次數(shù)指定為非整數(shù)值。對于可反轉(zhuǎn)動畫,每個動畫周期內(nèi)都包含從原始值到新值,然后再還原為原始值的動畫。如果希望動畫在新值上結(jié)束,則重復(fù)執(zhí)行次數(shù)要加0.5以增加半個額外動畫周期。

視圖過渡轉(zhuǎn)換動畫

視圖過渡轉(zhuǎn)換可以隱藏在視圖層中添加、刪除或顯示視圖帶來的視覺上的突然變化??梢允褂靡晥D過渡轉(zhuǎn)換來實現(xiàn)以下類型的更改:

  • 更改現(xiàn)有視圖的可見子視圖,使父視圖在不同狀態(tài)之間切換。
  • 當想使界面有很大的改變時,使用不同的視圖替換視圖層中的某個視圖。

注意:不要將視圖轉(zhuǎn)換與視圖控制器的跳轉(zhuǎn)相混淆,視圖轉(zhuǎn)換僅影響視圖層。

更改視圖的子視圖

在iOS 4之后,使用transitionWithView:duration:options:animations:completion:方法為視圖啟動過渡動畫。通常情況下,在此方法指定的動畫塊中,應(yīng)執(zhí)行與顯示、隱藏、添加或者刪除子視圖相關(guān)的動畫。這樣就能允許視圖對象創(chuàng)建視圖在更改之前和更改之后的截圖,并且會在這兩張截圖之間創(chuàng)建動畫。這種方式更加高效,但是,如果還需要對其他更改執(zhí)行動畫,則可以在調(diào)用此方法時配置UIViewAnimationOptionAllowAnimatedContent選項,這樣就可以防止視圖對象創(chuàng)建截圖,并直接對所有更改執(zhí)行動畫。

以下代碼是如何使用過渡轉(zhuǎn)換動畫使用戶界面看起來好像添加了新的文本輸入頁面的示例。用戶界面包含兩個嵌入的文本視圖,文本視圖的配置相同,但其中一個始終可見,另一個隱藏。當用戶點擊按鈕創(chuàng)建一個新頁面時,這個方法切換了兩個視圖的可見性,導(dǎo)致一個空的文本視圖的新空白頁面準備接收文本。轉(zhuǎn)換完成后,視圖使用私有方法保存舊頁面中的文本,并重置現(xiàn)在隱藏的文本視圖,以便稍后重新使用。

- (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:方法執(zhí)行視圖轉(zhuǎn)換動畫,代碼如下:

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

// Make your changes


[UIView commitAnimations];

另外,還需要設(shè)置好委托對象和動畫停止后執(zhí)行的回調(diào)方法。

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

iOS 4之后,使用transitionFromView:toView:duration:options:completion:方法在兩個視圖間過渡轉(zhuǎn)換。此方法實際上是從當前視圖層中刪除第一個視圖,然后插入另一個視圖。如果要隱藏視圖而不是從視圖層中刪除視圖,可以在調(diào)用此方法時配置UIViewAnimationOptionShowHideTransitionViews選項。

以下代碼展示了如何在單個視圖控制器管理的兩個主視圖之間交換顯示。視圖控制器的根視圖總是顯示兩個子視圖中的一個,每個視圖呈現(xiàn)的內(nèi)容相同,但界面布局不同。視圖控制器使用displayingPrimary成員變量(布爾值)來跟蹤在任何給定時間顯示哪個視圖。翻轉(zhuǎn)方向根據(jù)正在顯示的視圖而改變。

- (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;
        }
    }];
}

注意:除了交換視圖之外,還需要在視圖控制器中執(zhí)行代碼來管理主視圖和輔助視圖的加載和卸載。有關(guān)如何通過視圖控制器加載和卸載視圖的信息,可以參看View Controller Programming Guide for iOS

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

應(yīng)用程序可以根據(jù)需要自由地混合基于視圖和基于圖層的動畫代碼,但配置動畫參數(shù)的過程取決于誰擁有圖層。更改視圖擁有的圖層與更改視圖本身相同,并且應(yīng)用于圖層屬性的任何動畫都根據(jù)當前基于視圖的動畫塊的動畫參數(shù)來執(zhí)行。自定義圖層對象會忽略基于視圖的動畫塊參數(shù),而是使用默認的Core Animation參數(shù)。

如果要為所創(chuàng)建的圖層自定義動畫參數(shù),則必須直接使用Core Animation。通常,使用Core Animation動畫化圖層需要創(chuàng)建一個CABasicAnimation對象或者CAAnimation的其他子類對象,然后將該動畫對象添加到相應(yīng)的圖層。

以下代碼實現(xiàn)了一個動畫,其同時修改一個視圖和一個自定義圖層。視圖在其邊界的中心包含一個自定義CALayer對象。動畫順時針旋轉(zhuǎn)視圖,同時逆時針旋轉(zhuǎn)圖層。由于旋轉(zhuǎn)方向相反,圖層相對于屏幕保持其原始角度,看上去并沒有旋轉(zhuǎn)。

[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;
    }];
}];

注意:也可以在基于視圖的動畫塊之外創(chuàng)建并應(yīng)用CABasicAnimation對象,以獲得相同的結(jié)果。所有的動畫最終都依靠Core Animation來執(zhí)行。因此,如果動畫幾乎被同時提交,它們就會一起執(zhí)行。

其他

對應(yīng)用程序用戶界面的操作必須在主線程上執(zhí)行,也就是說必須在主線程中執(zhí)行UIView類的方法。創(chuàng)建視圖對象不一定要放在主線程,但其他所有操作都應(yīng)該在主線程上進行。

自定義打印輸出視圖信息,可以實現(xiàn)drawRect:forViewPrintFormatter:方法。有關(guān)如何支持打印輸出視圖的詳細信息,可以參看Drawing and Printing Guide for iOS。

Demo

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容