[轉] 關於UIView

【轉載】 原文地址 :http://blog.csdn.net/itianyi/article/details/8982518html

UIView是開發中使用得最多的控件了,深刻的理解頗有必要。ios

UIView是組成畫面的基本元素, NS_CLASS_AVAILABLE_IOS(2_0)@interface UIView :UIResponder<NSCoding,UIAppearance, UIAppearanceContainer> {數組

  @package緩存

    CALayer        *_layer;網絡

    id              _tapInfo;架構

    id              _gestureInfo;app

    NSMutableArray *_gestureRecognizers;框架

    NSArray        *_subviewCache;iphone

    float           _charge;異步

    NSInteger       _tag;

    UIViewController *_viewDelegate;

    NSString         *_backgroundColorSystemColorName;

    struct {

        unsigned int userInteractionDisabled:1;

        unsigned int implementsDrawRect:1;

        unsigned int implementsDidScroll:1;

        unsigned int implementsMouseTracking:1;

        unsigned int hasBackgroundColor:1;

        unsigned int isOpaque:1;

        unsigned int becomeFirstResponderWhenCapable:1;

        unsigned int interceptMouseEvent:1;

        unsigned int deallocating:1;

        unsigned int debugFlash:1;

        unsigned int debugSkippedSetNeedsDisplay:1;

        unsigned int debugScheduledDisplayIsRequired:1;

        unsigned int isInAWindow:1;

        unsigned int isAncestorOfFirstResponder:1;

        unsigned int dontAutoresizeSubviews:1;

        unsigned int autoresizeMask:6;

        unsigned int patternBackground:1;

        unsigned int fixedBackgroundPattern:1;

        unsigned int dontAnimate:1;

        unsigned int superLayerIsView:1;

        unsigned int layerKitPatternDrawing:1;

        unsigned int multipleTouchEnabled:1;

        unsigned int exclusiveTouch:1;

        unsigned int hasViewController:1;

        unsigned int needsDidAppearOrDisappear:1;

        unsigned int gesturesEnabled:1;

        unsigned int deliversTouchesForGesturesToSuperview:1;

        unsigned int chargeEnabled:1;

        unsigned int skipsSubviewEnumeration:1;

        unsigned int needsDisplayOnBoundsChange:1;

        unsigned int hasTiledLayer:1;

        unsigned int hasLargeContent:1;

        unsigned int unused:1;

        unsigned int traversalMark:1;

        unsigned int appearanceIsInvalid:1;

        unsigned int monitorsSubtree:1;

        unsigned int layoutEngineIsOverridden:1;

        unsigned int constraintsAreClean:1;

        unsigned int subviewLayoutConstraintsAreClean:1;

        unsigned int potentiallyHasDanglyConstraints:1;

        unsigned int doesNotTranslateAutoresizingMaskIntoConstraints:1;

        unsigned int autolayoutIsClean:1;

        unsigned int subviewsAutolayoutIsClean:1;

        unsigned int layoutFlushingDisabled:1;

        unsigned int layingOutFromConstraints:1;

        unsigned int wantsAutolayout:1;

        unsigned int subviewWantsAutolayout:1;

        unsigned int isApplyingValuesFromEngine:1;

        unsigned int isInAutolayout:1;

        unsigned int isUpdatingAutoresizingConstraints:1;

        unsigned int isUpdatingConstraints:1;

        unsigned int stayHiddenAwaitingReuse:1;

        unsigned int stayHiddenAfterReuse:1;

        unsigned int skippedLayoutWhileHiddenForReuse:1;

    } _viewFlags;

}

+ (Class)layerClass;   // default is [CALayer class]. Used when creating the underlying layer for the view.

- (id)initWithFrame:(CGRect)frame;   // default initializer


@property(nonatomic,getter=isUserInteractionEnabled)BOOL userInteractionEnabled; // default is YES. if set to NO, user events (touch, keys) are ignored and removed from the event queue.

@property(nonatomic)                                NSInteger tag;                // default is 0

@property(nonatomic,readonly,retain)                 CALayer  *layer;             // returns view's layer. Will always return a non-nil value. view is layer's delegate

@end

 

1,父類是UIResponder,說明全部的UIView都具備響應器的特性。

2,有一個對象    CALayer        *_layer;

先看看定義:

 

@interface CALayer : NSObject <NSCoding, CAMediaTiming>

{

@private

  struct _CALayerIvars {

    int32_t refcount;

    uint32_t magic;

    void *layer;

#if TARGET_OS_MAC && !TARGET_RT_64_BIT

    void *unused1[8];

#endif

  } _attr;

}

UIView之因此可以顯示出來,其實所有是靠CALayer的顯示。

當UIView須要顯示的時候,會調用drawRect:方法進行繪圖,而且會將全部內容繪製在本身的層上(CALayer上),系統將層拷貝到屏幕上,完成UIView的顯示。

經過操做這個CALayer對象,能夠很方便的調整UIView的一些界面屬性,好比:陰影,圓角大小,旋轉等等。

 

viewT.layer.cornerRadius = 10;//設置半徑
viewT.layer.borderWidth = 10;//設置邊框的寬度,固然能夠不要
viewT.layer.borderColor = [[UIColor redColor] CGColor];//設置邊框的顏色
viewT.layer.masksToBounds = YES;//設爲NO去試試

maskToBounds屬性決定子圖層是否相對於父圖層裁剪

CALayer能夠添加層,跟UIView同樣,UIView能夠添加子視圖,那麼怎麼選擇是食用UIView仍是UILayer來實現呢?

對比CALayer,UIView多了一個事件的處理功能,由於UIView是UIResponder的子類,若是有交互必須使用UIView,CALayer效率上很高。


3,UIView的setNeedsDisplay和setNeedsLayout方法

 

  一、在Mac OS中NSWindow的父類是NSResponder,而在i OS 中UIWindow 的父類是UIVIew。程序通常只有一個窗口可是會又不少視圖。

  二、UIView的做用:描畫和動畫,視圖負責對其所屬的矩形區域描畫、佈局和子視圖管理、事件處理、能夠接收觸摸事件、事件信息的載體、等等。

  三、UIViewController 負責建立其管理的視圖及在低內存的時候將他們從內存中移除。還爲標準的系統行爲進行響應。

  四、layOutSubViews 能夠在本身定製的視圖中重載這個方法,用來調整子視圖的尺寸和位置。

  五、UIView的setNeedsDisplay和setNeedsLayout方法。首先兩個方法都是異步執行的。setNeedsDisplay會調用自動調用drawRect方法,這樣能夠拿到UIGraphicsGetCurrentContext,就能夠畫畫了。而setNeedsLayout會默認調用layoutSubViews,就能夠處理子視圖中的一些數據。

綜上所述:setNeedsDisplay方便繪圖,而layoutSubViews方便出來數據

setNeedDisplay告知視圖它發生了改變,須要從新繪製自身,就至關於刷新界面.


4,initWithFram:(CGRect)frame方法用來初始化一個UIView,傳入CGRect當參數


5,能夠看出UIView常用的屬性沒有定義在類裏面,那確定是定義在類別裏面了:

 

@interface UIView(UIViewGeometry)

@property(nonatomic)CGRect            frame;

@property(nonatomic)CGRect            bounds;     // default bounds is zero origin, frame size. animatable

@property(nonatomic)CGPoint           center;   

。。。

@end

能夠看到UIView經常使用的屬性在這個類別裏面。

注意:雖然ios6.0的時候增長了一個新功能,若是在類定義的時候聲明屬性以後,會自動生成一個如下劃線開頭的成員變量,可是對於類別是無效的。

類別只能添加方法,不能添加成員變量的。因此這裏添加的屬性只不過是UIView的getter/setter方法而已,並無_frame或者_bounds這種成員變量。

只有view.frame 相似的點方法。返回的是一個CGRect的結構體

struct CGRect {

  CGPoint origin;

  CGSize size;

};

typedef struct CGRect CGRect;

這個結構體的值多是存放在CALayer對象的void *unused1[8];指針數組中的某個指針指向的數據裏。(僅猜想)


6,UIView一些經常使用方法:

@interface UIView(UIViewHierarchy)

@property(nonatomic,readonly)UIView       *superview;

@property(nonatomic,readonly,copy)NSArray *subviews;

@property(nonatomic,readonly)UIWindow     *window;

 

- (void)addSubview:(UIView *)view;

- (void)removeFromSuperview;

- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;

- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2;

- (void)bringSubviewToFront:(UIView *)view;

- (void)sendSubviewToBack:(UIView *)view;

- (void)didAddSubview:(UIView *)subview;

- (void)willRemoveSubview:(UIView *)subview;

- (BOOL)isDescendantOfView:(UIView *)view; // returns YES for self.

- (UIView *)viewWithTag:(NSInteger)tag;    // recursive search. includes self

// Allows you to perform layout before the drawing cycle happens. -layoutIfNeeded forces layout early

- (void)setNeedsLayout;

- (void)layoutIfNeeded;

- (void)layoutSubviews; 

@end


@interface UIView(UIViewRendering)

- (void)drawRect:(CGRect)rect;

- (void)setNeedsDisplay;

- (void)setNeedsDisplayInRect:(CGRect)rect;

 

@property(nonatomic,getter=isHidden)BOOL              hidden;        

@property(nonatomic)                BOOL              clipsToBounds;             // When YES, content and subviews are clipped to the bounds of the view. Default is NO.

@property(nonatomic,copy)           UIColor          *backgroundColor;            // default is nil

@property(nonatomic)                CGFloat           alpha;   

@property(nonatomic)                 CGRect            contentStretch 

@end


@interface UIView(UIViewAnimation) //動畫

+ (void)beginAnimations:(NSString *)animationID context:(void *)context;  // additional context info passed to will start/did stop selectors. begin/commit can be nested

+ (void)commitAnimations;  

+ (void)setAnimationDuration:(NSTimeInterval)duration;             // default = 0.2

+ (void)setAnimationDelay:(NSTimeInterval)delay;     

@end


@interface UIView(UIViewAnimationWithBlocks)

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completionNS_AVAILABLE_IOS(4_0);

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completionNS_AVAILABLE_IOS(4_0);// delay = 0.0, options = 0

@end


@interface UIView (UIViewGestureRecognizers)//爲UIView添加手勢

@property(nonatomic,copy) NSArray *gestureRecognizersNS_AVAILABLE_IOS(3_2);

- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizerNS_AVAILABLE_IOS(3_2);

- (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizerNS_AVAILABLE_IOS(3_2);

@end

只是摘錄了一部分經常使用的。




如下內容 爲官方文檔翻譯(來源於網絡),若是你英文還能夠,請看英文地址:英文地址

 

     曾經有人這麼說過,在iphone裏你看到的,摸到的,都是UIView,因此UIView在iphone開發裏具備很是重要的做用。那麼UIView咱們到底知道多少呢。請看看下面的問題,
若是這些你都知道,那麼本文章的內容就請繞道,若是你還不太清楚,我想看了下面的內容,你就明白了。
1。bounds和frame分別表示什麼?
2。ContentMode裏UIViewContentModeScaleToFill表明什麼?
3。contentStretch 裏的指定UIView裏縮放區域是如何計算的?
4。UIVIew裏的哪些屬性變化能夠用動畫來呈現?
5。UIKit的座標系和Core Graphics的座標系的差異是什麼?

 

     視圖和窗口展現了應用的用戶界面,同時負責界面的交互。UIKit和其餘系統框架提供了不少視圖,你能夠就地使用而幾乎不須要修改。當你須要展現的內容與標準視圖容許的有很大的差異時,你也能夠定義本身的視圖。

無論你是使用系統的視圖仍是建立本身的視圖,你須要理解UIView和UIWindow類所提供的基本結構。這些類提供了複雜的方法來管理視圖的佈局和展現。理解這些方法的工做很是重要,使你在應用發生改變時能夠確認視圖有合適的行爲。

 視圖架構 fundamentals 

大部分你想要可視化操做都是由視圖對象-即UIView 類的實例-來進行的。一個視圖對象定義了一個屏幕上的一個矩形區域,同時處理該區域的繪製和觸屏事件。一個視圖也能夠做爲其餘視圖的父視圖,同時決定着這 些子視圖的位置和大小。UIView類作了大量的工做去管理這些內部視圖的關係,可是須要的時候你也能夠定製默認的行爲。

視圖與Core Animation層聯合起來處理着視圖內容的解釋和動畫過渡。每一個UIKit框架裏的視圖都被一個層對象支持(一般是一個CALayer類的實例),它 管理管理着後臺的視圖存儲和處理視圖相關的動畫。然而,當你須要對視圖的解釋和動畫行爲有更多的控制權時,你可使用層。

爲了理解視圖和層之間的關係,咱們能夠藉助於一些例子。 圖1-1顯示了ViewTransitions樣例程序的視圖層次及其對底層Core Animation層的關係。應用中的視圖包括了一個window(同時也是一個視圖),一個通用的表現得像一個容器視圖的UIView對象,一個圖像視 圖,一個控制顯示用的工具條,和一個工具條按鈕(它自己不是一個視圖可是在內部管理着一個視圖)。(注意這個應用包含了一個額外的圖像視圖,它是用來實現 動畫的)。爲了簡化,同時由於這個視圖一般是被隱藏的,因此沒把它包含在下面的圖中。每一個視圖都有一個相應的層對象,它能夠經過視圖礶r屬性被訪問。(因 爲工具條按鈕不是一個視圖,你不能直接訪問它的層對象。)在它們的層對象以後是Core Animation的解釋對象,最後是用來管理屏幕上的位的硬件緩存。 

Figure 1-1 View architecture

 

 

使用Core Animation的層對象有很重要的性能意義。一個視圖對象的繪製代碼須要儘可能的少被調用,當它被調用時,其繪製結果會被Core Animation緩存起來並在日後能夠被儘量的重用。重用已經解釋過的內容消除了一般須要更新視圖的開銷昂貴的繪製週期。內容的重用在動畫中特別重 要,咱們可使用已有的內容,這樣比建立新的內容開銷更小。

 

視圖層次和子視圖管理

 

除了提供本身的內容以外,一個視圖也能夠表現得像一個容器。當一個視圖包含其餘視圖時,就在兩個視圖之間建立了一個父子關係。在這個關係中孩子視圖被看成子視圖,父視圖被看成超視圖。建立這樣一個關係對應用的可視化和行爲都有重要的意義。

在視覺上,子視圖隱藏了父視圖的內容。若是子視圖是徹底 不透明的,那麼子視圖所佔據的區域就徹底的隱藏了父視圖的相應區域。若是子視圖是部分透明的,那麼兩個視圖在顯示在屏幕上以前就混合在一塊兒了。每一個父視圖 都用一個有序的數組存儲着它的子視圖,存儲的順序會影響到每一個子視圖的顯示效果。若是兩個兄弟子視圖重疊在一塊兒,後來被加入的那個(或者說是排在子視圖數 組後面的那個)出如今另外一個上面。

父子視圖關係也影響着一些視圖行爲。改變父視圖的尺寸會連帶着改變子視圖的尺寸和位置。在這種狀況下,你能夠經過合適的配置視圖來重定義子視圖的尺寸。其餘會影響到子視圖的改變包括隱藏父視圖,改變父視圖的alpha值,或者轉換父視圖。

視圖層次的安排也會決定着應用如何去響應事件。在一個具 體的視圖內部發生的觸摸事件一般會被直接發送到該視圖去處理。然而,若是該視圖沒有處理,它會將該事件傳遞給它的父視圖,在響應者鏈中以此類推。具體視圖 可能也會傳遞事件給一個干預響應者對象,像視圖控制器。若是沒有對象處理這個事件,它最終會到達應用對象,此時一般就被丟棄了。

獲取更多關於如何建立視圖層次,查看 creating and managing a view hierarchy

 

視圖繪製週期

 

UIView類使用一個點播繪製模型來展現內容。當一個 視圖第一次出如今屏幕前,系統會要求它繪製本身的內容。在該流程中,系統會建立一個快照,這個快照是出如今屏幕中的視圖內容的可見部分。若是你歷來沒有改 變視圖的內容,這個視圖的繪製代碼可能永遠不會再被調用。這個快照圖像在大部分涉及到視圖的操做中被重用。

若是你確實改變了視圖內容,也不會直接的從新繪製視圖內 容。相反,使用setNeedsDisplay或者setNeedsDisplayInRect:方法廢止該視圖,同時讓系統在稍候重畫內容。系統等待當 前運行循環結束,而後開始繪製操做。這個延遲給了你一個機會來廢止多個視圖,從你的層次中增長或者刪除視圖,隱藏,重設大小和重定位視圖。全部你作的改變 會稍候在同一時間反應。

注意:改變一個視圖的幾何結構不會自動引發系統重畫內 容。視圖的contentMode屬性決定了改變幾何結構應該若是解釋。大部份內容模式在視圖的邊界內拉伸或者重定位了已有快照,它不會從新建立一個新的 快照。獲取更多關於內容模式若是影響視圖的繪製週期,查看 content modes 

 

當繪製視圖內容的時候到了時,真正的繪製流程會根據視圖 及其配置改變。系統視圖一般會實現私有的繪製方法來解釋它們的視圖,(那些相同的系統視圖常常開發接口,好讓你能夠用來配置視圖的真正表現。)對於定製的 UIView子類,你一般能夠覆蓋drawRect:方法並使用該方法來繪製你的視圖內容。也有其餘方法來提供視圖內容,像直接在底部的層設置內容,可是 覆蓋drawRect:時最通用的技術

 

內容模式

 

視圖的內容模式控制着視圖如何回收內容來響應視圖幾何結 構的變化,也控制着是否須要回收內容。當一個視圖第一次顯示時,它一般會解釋內容,其結果會被底層的層級樹捕獲爲一張位圖。在那以後,改變視圖的幾何結構 不會致使從新建立位圖。相反,視圖中contentMode屬性的值決定着這張位圖是否該被拉伸,以適應新的邊界或者只是簡單的被放到角落或者視圖的邊 界。

 

視圖的內容模式在你進行以下操做時被應用:

 

改變視圖frame或者bounds矩形的寬度或者高度時。

 

賦值給視圖的transform屬性,新的轉換包括一個放縮因子。

 

大部分視圖的contentMode值是 UIViewContentModeScaleToFiill,它使視圖的內容被放縮到適合新框架的值。Figure 1-2展現了使用其餘可用的內容模式的結果。正如你在圖中所看到的那樣,不是全部的內容模式均可以填充視圖的範圍,能夠的模式可能會扭曲內容。

 

內容模式很好的支持了視圖的內容回收,可是當你想視圖在 放縮和重設尺寸的操做中重繪你也能夠用UIViewContentModeRedraw內容模式。設置這個值繪強制系統調用視圖的drawRect:方法 來響應幾何結構的變化。一般來說,你應該儘量的避免使用這個模式,同時你不該該在標準的系統視圖中使用這個模式。

 

獲取更多骨幹與可用的內容模式,查看UIView Class Reference

 

Figure 1-2

 

 

拉伸視圖

 

你能夠指定視圖的某部分爲可拉伸的,以便當視圖的尺寸改 變時只有可拉伸的部分被影響到。可拉伸的部分一般給按鈕或者其餘的部分爲重複模式的視圖。由你指定的可拉伸區域容許沿着兩條或者其中一條軸拉伸。固然,當 一個視圖沿着兩條軸拉伸的時候,視圖的邊界必須也定義了一個重複的模式來避免任何的扭曲。Figure1-3展現了這種扭曲在視圖裏是怎麼表現本身的。每 個視圖裏的原始像素的顏色都自我複製,以即可以填充更大視圖的相應區域。

 

Figure 1-3 拉伸一個按鈕的背景

 

 

你能夠用contentStretch屬性來定義一個視 圖的可拉伸區域。這個屬性的值一個邊的值被標準化爲0.0到1.0之間的矩形。當拉伸這個視圖時,系統將視圖的當前邊界值和放縮因子乘以標準值,以便決定 哪些像素須要被拉伸。使用標準值能夠減輕每次改變視圖的邊界值都更新contentStretch屬性的須要。

 

視圖的內容模式也在決定如何視圖的可拉伸區域的使用中扮 演着重要的角色。只有當內容模式可能繪引發視圖內容放縮的時候可拉伸區域纔會被使用。這意味這你的可拉伸視圖只被 UIViewContentModeScaleToFill, UIViewContentModeScaleAspectFit和UIViewContentModeScaleAspectFill內容模式。若是你 指定了一個將內容彈到邊界或者角落的內容模式(這樣就沒有真正的放縮內容),這個視圖會忽視可拉伸區域。

 

注意:當須要建立一個可拉伸UIImage對象做爲視圖的背景時,使用contentStretch屬性是推薦的。可拉伸視圖徹底被Core Animation層處理,這樣性能一般更好。

 

嵌入式動畫支持

 

使用層對象來支持視圖的其中一個利益是你能夠輕鬆的用動 畫處理視圖相關的改變。動畫是與用戶進行信息交流的一個有用的方法,並且應該老是在進行應用設計的過程當中考慮使用動畫。UIView類的不少屬性是動畫化 的-也就是,能夠半自動的從一個值動畫的變化到另外一個值。爲了實現這樣一個動畫,你須要作的只是:

1 告訴UIKit你想要實現一個動畫

2 改變這個屬性的值

在一個UIView對象中有如下的動畫化屬性:

frame - 你可使用這個來動畫的改變視圖的尺寸和位置

bounds - 使用這個能夠動畫的改變視圖的尺寸

center - 使用這個能夠動畫的改變視圖的位置

transform - 使用這個能夠翻轉或者放縮視圖

alpha - 使用這個能夠改變視圖的透明度

backgroundColor - 使用這個能夠改變視圖的背景顏色

contentStretch - 使用這個能夠改變視圖內容如何拉伸

 

動畫的一個很重要的地方是用於從一組視圖到另外一組視圖的 過渡。一般來講,會用一個視圖控制器來管理關係到用戶界面的主要變動的動畫。例如,涉及到從高層到底層信息的導航的界面,一般會使用一個導航控制器來管理 視圖的過渡,這些視圖顯示了數據的每個連續層面。然而,你也可使用動畫來建立兩組視圖的過渡,而不是視圖控制器。當你想用一個系統提供的視圖控制器無 法支持的導航方案時你可能會這樣作。

 

除了用UIKit類能夠建立動畫外,你也能夠用Core Animation層來建立動畫。在更低層你有更多的在時間或者動畫屬性上的控制權。

 

獲取更多關於如何建立一個基於視圖的動畫,查看 Animations

獲取更多關於使用Core Animation建立動畫的信息,查看Core Animation Programming Guide和Core Animation Cookbook.

 

視圖幾何結構和座標系統

 

 

UIKit的默認座標系統把原點設置在左上角,兩條軸往 下和右擴展。作標誌被表示爲浮點數,這樣容許內容的精確佈局和定位而無論底層的屏幕。Figure1-4展現了相對於屏幕的座標系統。除了屏幕座標系統窗 口和視圖也定義了它們本身的本地座標系統,這樣容許你指定相對於視圖或者窗口原點的座標而不是屏幕。

Figure 1-4 UIKit中的座標系統 

由於每一個視圖和窗口都定義了它本身的本地座標系統,你需 要留意在任什麼時候間內是哪一個座標系統在起做用。每次繪製或者改變一個視圖都是基於一個座標系統的。在某些繪製中會基於視圖自己的座標系統。在某些幾何結構變 更中是基於父視圖的座標系統的。UIWindow和UIView類都包含了幫助你從一個座標系統轉換到另外一個的方法。

重要:一些iOS技術定義了默認的座標系統,它們的原點和方向與UIKit的不一樣。;例如,Core Graphics和OpenGL ES的座標系統是原點在可視區域的左下角,而y軸往上遞增。當繪製或者建立內容時,你的代碼應該考慮到一些不一樣而且適應座標值。

  frame, bounds和center屬性之間的關係

視圖對象使用frame, bounds和center屬性來跟蹤它的尺寸和位置:

frame屬性包含了frame矩形,指定了在父視圖座標系統中該視圖的尺寸和位置。

center屬性包含了在父視圖座標系統中的已知中心點。

bounds屬性包含了邊界矩形,指定了在視圖本地座標系統中視圖的尺寸。

主要使用center和frame屬性來控制當前視圖的 幾何結構。例如,當在運行時構建你的視圖層次或者改變視圖的尺寸或者位置時你可使用這些屬性。若是你只是要改變視圖的位置,那麼推薦使用center屬 性。center屬性的值永遠是可用的,即便添加了放縮或者轉換因子到視圖的轉換矩陣當中。可是對於frame屬性卻不是,當視圖的轉換矩形不等於原始矩 陣時它被看成時無效的。

 

在繪製的過程當中主要使用bounds屬性。這個邊界矩陣 在視圖的本地座標系統被解釋。這個矩形的默認原點是(0, 0),它的尺寸也適應frame矩形的尺寸。任何繪製在這個矩形當中的東西都是該視圖的可視內容的一部分。若是你改變了bounds矩形的原點,任何你繪 制在新矩形的東西都會變成該視圖可視內容的一部分。

Figure1-5展現了一個圖像視圖的frame和bounds矩形之間的關係。圖中,圖像視圖的右上角被定位在父視圖座標系統的(40, 40),它的矩形尺寸爲240x380。對於bounds矩形,原點是(0, 0),矩形尺寸也是240x380。

Figure 1-5 視圖frame和bounds之間的關係

 

 

即便你能夠獨立的改變frame,bounds和center屬性,其中一個改變仍是會影響到另外兩個屬性:

當你設置了frame屬性,bounds屬性的尺寸值也改變來適應frame矩形的新尺寸。center屬性也會改變爲新frame矩形的中心值。

當你設置了center屬性,frame的原點也會相應的改變。

當你設置了bounds屬性,frame屬性會改變以適應bounds矩形的新尺寸。

視圖的框架默認不會被它的父視圖框架裁剪。這樣的化,任 何放置在父視圖外的子視圖都會被完整的解釋。你能夠改變這種行爲,改變父視圖的clipsToBounds屬性就能夠。無論子視圖是否在視覺上被裁剪,觸 屏事件老是發生在目標視圖父視圖的bounds矩形。換句話說,若是觸摸位於父視圖外的那部分視圖,那麼該事件不會被髮送到該視圖。

座標系統轉換矩陣

座標系統轉換矩陣給改變視圖(或者是它的視圖)提供了一 個輕鬆和簡易的方法。一個仿射轉換是一個數學矩陣,它指定了在座標系統中的點是怎麼被映射到另外一個座標系統中的點。你能夠對整個視圖應用仿射轉換,以基於 其父視圖來改變視圖的尺寸,位置或者朝向。你也能夠在你的繪製代碼中應用仿射轉換,以對已解釋內容的獨立部分實現相同的操控。如何應用仿射轉換是基於這樣 的上下文的:

爲了修改整個視圖,能夠修改視圖transform屬性的仿射轉換值。

爲了在視圖中的drawRect:方法中修改內容的指定部分,能夠修改與當前圖形上下文相關的仿射轉換。

當你想實現動畫時,一般能夠修改視圖的 transform屬性值。例如,你可使用這個屬性來製做一個視圖圍繞中心點翻轉的動畫。你不該該在其父視圖的座標空間中用這個屬性來永久的改變你的視 圖,像修改它的位置和尺寸。對於這種類型的改變,你能夠修改視圖的frame矩形。

注意:當修改視圖的transform屬性值時,全部的轉換都是基於視圖的中心點來實現的。

在視圖的drawRect:方法中,你可使用仿射轉換 來定位或者翻轉你想要繪製的項目。相對於在視圖某些部位中修正對象的位置,咱們更傾向於相對於一個固定點去建立對象,一般是(0, 0),同時在繪製以前使用轉換來定位對象。這樣的話,若是在視圖中對象的位置改變了,你要作的只是修改轉換矩陣,這樣比爲對象從新建立新的位置性能更好開 銷更低。你能夠經過使用CGContextGetCTM方法來獲取關於圖形上下文的仿射轉換,同時能夠用Core Graphics的相關方法在繪製中來設置或者修改這個轉換矩陣。

當前轉換矩陣(CTM)是一個在任什麼時候候都被使用的仿射矩陣。當操控整個視圖的幾何結構時,CTM就是視圖transform屬性的值。在drawRect:方法中,CTM是關於圖形上下文的仿射矩陣。

每一個子視圖的座標系統都是構建在其祖先的座標系統之上 的。因此當你修改一個視圖的transform屬性,這個改變會影響到視圖及其全部的子視圖。然而,這些改變只會影響到屏幕上視圖的最終解釋。由於每一個視 圖都負責繪製本身的內容和對本身的子視圖進行佈局,因此在繪製和佈局的過程當中它能夠忽略父視圖的轉換。

Figure1- 6描述了在解釋的時候,兩個不一樣的轉換因子是如何在視覺上組合起來的。在視圖的drawRect:方法中,對一個形狀應用一個45度的轉換因子會使該形狀 翻轉指定的角度。另外加上一個45度的轉換因子會致使整個形狀翻轉90度。這個形狀對於繪製它的視圖來說仍然只是翻轉了45度,可是視圖本身的轉換讓它看 起來像使翻轉了90度。

Figure 1-6 翻轉一個視圖和它的內容

 

 

重要:若是一個視圖的transform屬性不是其定義 時轉換矩陣,那麼視圖的frame屬性是未定義的並且必須被忽略。當對視圖應用轉換時,你必須使用視圖的bounds和center屬性來獲取視圖的位置 和尺寸。子視圖的frame矩形仍然是有效的,由於它們與視圖的bounds相關。

 

獲取更多關於在運行時修改視圖的transform屬 性,查看 「Translating, Scaling, and Rotating Views.」獲取更多如何在繪製過程當中使用轉換來定位內容,查看 Drawing and Printing Guide for iOS.

 

點與像素

在iOS中,全部的座標值和距離都被指定爲使用浮點數,其單元值稱爲點。點的數量隨着設備的不一樣而不一樣,並且彼此不相關。要明白關於點的最主要一點是它們提供了一個繪製用的固定框架。

Table 1-1 列出了不一樣iOS設備的分辨率(點度量)。前爲寬後爲長。只要你依照這些屏幕的尺寸來設計用戶界面,你的視圖就回被相應的設備正確顯示。

 

Table 1-1 

 

 

Device                                            Screen dimensions (in points)

iPhone and iPod touch                      320 x 480

iPad                                               768 x 1024

 

 

每一種使用基於點度量系統的設備都定義了一個用戶座標空 間。這是幾乎在你全部的代碼都會用到的標準座標空間。例如,當你要操控視圖的幾何結構或者調用Core Graphics方法來繪製內容時會用到點和用戶座標空間。即便有時用戶座標空間裏的座標時直接映射到設備屏幕的像素,你仍是永遠不該該假設這是永遠不變 的。相反,你應該記住:一個點並不必定對應着屏幕上的一個像素

在設備層面,全部由你指定的視圖上的座標在某些點上必須 被轉化成像素。然而,從用戶座標空間上的點到設備座標空間上的像素一般由系統來處理。UIKit和Core Graphics都主要使用基於向量的繪製模型,全部的座標值都被指定爲使用點。這樣,若是你用Core Graphics畫了一條曲線,你會用一些值來指定這條曲線,而無論底層屏幕使用怎樣的解決方法。

當你須要處理圖像或者其餘基於像素的技術,像 OpenGL ES時,iOS會幫你管理這些像素。對於存儲爲應用程序的束中的資源的靜態圖像文件,iOS定義了一些約定,能夠指定不一樣像素密度的圖像,也能夠在加載圖 像時最大限度的適應當前屏幕的解決方案。視圖也提供了關於當前放縮因子的信息,以便你能夠適當的調整任何基於像素的繪製代碼來適應有更高級解決方案的屏 幕。在不一樣屏幕的解決方案中處理基於像素內容的技術能夠在"Supporting High-Resolution Screens"和"Drawing and Printing Guide for iOS"找到描述。 

 

視圖的運行時交互模型 

 

當用戶和界面進行交互時,或者由代碼程序性的改變一些東 西時,一系列複雜的事件就會發生在UIKit的內部來處理這些交互。在這個系列中的某些點,UIKit喚出你的視圖類,同時給它們一個機會去響應程序的行 爲。理解這些喚出點對於理解視圖在哪裏融入系統很重要。Figure 1-7 展現了這些事件的基本序列,從用戶觸屏開始到圖形系統更新屏幕內容來響應結束。一樣的事件序列也會發生在任何程序性啓動的動做。

Figure 1-7 UIKit 與視圖對象進行交互

 

 

如下的步驟分解了圖1-7中的事件序列,既解釋了在每一步發生了什麼,也解釋了應用如何響應

1 用戶觸屏

2 硬件報告觸摸事件給UIKit框架

3 UIKit框架將觸摸事件打包成UIEvent對象,同時分發給適合的視圖。(對於UIKit框架如何提交事件給視圖的詳細解釋,查看 Event Handing Guide for iOS)

4 視圖中的事件處理代碼可能進行如下的動做來響應:

改變視圖或者其子視圖的屬性(frame, bounds, alpha, 等等)

調用setNeedsLayout方法以標記該視圖(或者它的子視圖)爲須要進行佈局更新

調用setNeedsDisplay或者setNeedsDisplayInRect:方法以標記該視圖(或者它的子視圖)須要進行重畫

通知一個控制器關於一些數據的更新

固然,哪些事情要作,哪些方法要被調用是由視圖來決定的。

5 若是一個視圖的幾何結構改變了,UIKit會根據如下幾條規則來更新它的子視圖:

a 若是自動重設尺寸的規則在發生做用,UIKit會根據這些規則來調整視圖。獲取更多關於自動重設尺寸規則如何工做,查看"Handling Layout Changes Automatically Using Autoresizing Rules."

b 若是視圖實現了layoutSubviews方法,UIKit會調用它。你能夠在你的定製視圖中覆蓋這個方法同時用它來調整任何子視圖的位置和大小。例 如,一個提供了巨大滾動區域的視圖會須要使用幾個子視圖做爲「瓦塊」而不是建立一個不太可能放進內存的巨大視圖。在這個方法的實現中,視圖會隱藏任何屏幕 外的子視圖,或者重定位它們而後用來繪製新的可視內容。做爲這個流程的一部分,視圖的佈局代碼也能夠廢止任何須要被重畫的視圖。

6 若是任何視圖的任何部分被標記爲須要重畫,UIKit會要求視圖重畫自身。

對於顯式的定義了drawRect:方法的定製視圖,UIKit會調用這個方法。這方法的實現應該儘快重畫視圖的指定區域,而且不該該再作其餘事。不要在這個點上作額外的佈局,也不要改變應用的數據模型。提供這個方法僅僅是爲了更新視圖的可視內容。

標準的系統視圖一般不會實現drawRect:方法,可是也會在這個時候管理它們的繪製。

 

7 任何已經更新的視圖會與應用餘下的可視內容組合在一塊兒,同時被髮送到圖形硬件去顯示。

8 圖形硬件將已解釋內容轉化到屏幕上。

 

注意:上面的更新模型主要應用於使用標準系統視圖和繪製 技術的應用。使用OpenGL ES來繪製的應用一般會配置一個單一的全屏視圖和直接繪製相關的OpenGL圖像上下文。你的視圖仍是應該處理觸屏事件,可是它是全屏的,毋需給子視圖布 局或者實現drawRect:方法。獲取更多關於使用OpenGL ES的信息,查看 OpenGL ES Programming Guide for iOS.

 

給定以前的一系列步驟,將本身的定製視圖整合進去的方法包括:

事件處理方法:

touchesBegan:withEvent:

touchesMoved:withEvent:

touchesEnded:withEvent:

touchesCancelled:withEvent:

layoutSubviews方法

drawRect:方法

這些是視圖的最經常使用的覆蓋方法,可是你可能不須要覆蓋全 部。若是你使用手勢識別來處理事件,你不須要覆蓋事件處理方法。類似的,若是你的視圖沒有包含子視圖或者它的尺寸不會改變,那就沒有理由去覆蓋 layoutSubviews方法。最後,只有當視圖內容會在運行時改變,同時你要用UIKit或者Core Graphics等本地技術來繪製時才須要用到drawRect。

 

要記住這些是主要的整合點,可是不只僅只有這些。UIView類中有些方法是專門設計來給子類覆蓋的。你應該到UIView Class Reference中查看這些方法的描述,以便在定製時清楚哪些方法適合給你覆蓋。

 

有效使用視圖的提示

 

當你須要繪製一些標準系統視圖不能提供的內容時,定製視圖是頗有用的。可是你要負責保證視圖的性能要足夠的高。UIKit會盡量的優化視圖相關的行爲,也會幫助你提升性能。然而,考慮一些提示能夠幫助到UIKit。 

重要:在調整繪製代碼以前,你應該一直收集與你視圖當前性能有關的數據。估量當前性能讓你能夠肯定是否真的有問題,同時若是真的有問題,它也提供一個基線,讓你在將來的優化中能夠比較。

視圖不會老是有一個相應的視圖控制器

在應用中,視圖和視圖控制器之間的一對一關係是不多見的。視圖控制器的工做是管理一個視圖層次,而視圖層次常常是包含了多個視圖,它們都有自包含特性。對於iPhone應用,每一個視圖層次一般都填滿了整個屏幕,儘管對於iPad應用來講不是。 

當你設計用戶界面的時候,考慮到視圖控制器的所扮演的角色是很重要的。視圖控制器提供了不少重要的行爲,像協調視圖的展現,協調視圖的剔除,釋放內存以響應低內存警告,還有翻轉視圖以響應界面的方向變動。逃避這些行爲會致使應用發生錯誤。

獲取更多關於視圖控制器的信息,查看 View Controller Programming Guide for iOS 

最小化定製的繪畫

儘管定製的繪畫有時是須要的,可是你也應該儘可能避免它。真正須要定製繪畫的時候是已有的視圖類沒法提供足夠的表現和能力時。任什麼時候候你的內容都應該能夠被組裝到其餘視圖,最好結果時組合那些視圖對象到定製的視圖層次 

利用內容模式 

內 容模式能夠最小化重畫視圖要花費的時間。默認的,視圖使用UIViewContentModeScaleToFill 內容模式,這個模式會放縮視圖的已有內容來填充視圖的frame矩形。須要時你能夠改變這個模式來調整你的內容,可是應該避免使用 UIViewContentModeRedraw內容模式。無論哪一個內容模式發生做用,你均可以調用setNeedsDisplay或者 setNeedsDisplayInRect:方法來強制視圖重畫它的內容

可能的話將視圖聲明爲不透明

UIKit使用opaque屬性來決定它是否能夠優化組 合操做。將一個定製視圖的這個屬性設置爲YES會告訴UIKit不須要解釋任何在該視圖後的內容。這樣能夠爲你的繪製代碼提升性能而且是推薦的。固然,如 果你將這個屬性設置爲YES,你的視圖必定要用不透明的內容徹底填充它的bounds矩形。

滾動時調整視圖的繪製行爲

滾動會致使數個視圖在短期內更新。若是視圖的繪製代碼 沒有被適當的調整,滾動的性能會很是的緩慢。相對於老是保證視圖內容的平庸,咱們更傾向於考慮滾動操做開始時改變視圖行爲。例如,你能夠暫時減小已解釋的 內容,或者在滾動的時候改變內容模式。當滾動中止時,你能夠將視圖返回到前一狀態,同時須要時更新內容。 

不要嵌入子視圖來定製控制 

儘管在技術上增長子視圖到標準系統控制對象-繼承自 UIControl的類-是可行的,你仍是永遠不該該用這種方法來定製它們。控制對象支持定製,它們有顯式而且良好歸檔的接口。例如,UIButton類 包含了設置標題和背景圖片的方法。使用已定義好的定製點意味着你的代碼老是會正確的工做。不用這些方法,而嵌入一個定製的圖像視圖或者標籤到按鈕中去會導 致應用出現未預期的結果。

相關文章
相關標籤/搜索