- iPhone UIKit詳解
- 王志剛 王中元 朱蕾編著
- 1862字
- 2019-01-01 07:04:24
2.4 UIView的外觀
2.4.1 外觀定制
繼承UIView的子類中,可以擁有自己獨特的各種子元素。但是當UIView通過其frame屬性任意改變其位置以及尺寸的情況下,其子元素的位置往往也會出現變化,甚至出現混亂。問題是,我們到底有沒有辦法避免這種混亂情況的出現呢?
首先我們能想到的是,提前編寫些調整UIView外觀的方法,當frame屬性發生改變后及時調用這些方法。只要不忘調用這些方法,就可以實現我們的目的。實際上,UIView中已經提供了這種調整外觀用的方法。而且是當UIView的尺寸發生變化,需要重新調整外觀的時候會自動調用此方法。這個外觀自動調整的方法為layoutSubviews。在UIView的子類中,只需要重新調用layoutSubviews方法,就可以實現外觀的自動調整。以下是重寫調用layoutSubviews方法的實例代碼。
// 定義UIView的子類 // 子類中擁有child1_以及child2_兩個標簽子元素 @interface LayoutTest :UILabel { @private UILabel* child1_; UILabel* child2_; } @end // 實現LayoutTest類 @implementation LayoutTest // 釋放處理 -(void)dealloc { [child1_ release]; [child2_ release]; [super dealloc]; } // 初始化處理 -(id)init { if((self = [super init])){ // 調整自己的大小,追加兩個標簽 self.frame = CGRectMake(0,0,320,320); child1_ = [[UILabel alloc] initWithFrame:CGRectZero]; child1_.text = @"CHILD 1"; [child1_ sizeToFit]; child1_.backgroundColor = [UIColor redColor]; child1_.textColor = [UIColor whiteColor]; child2_ = [[UILabel alloc] initWithFrame:CGRectZero]; child2_.text = @"CHILD 2"; [child2_ sizeToFit]; child2_.backgroundColor = [UIColor blueColor]; child2_.textColor = [UIColor whiteColor]; child2_.center = CGPointMake(child2_.center.x,child2_.center.y + 30); [self addSubview:child1_]; [self addSubview:child2_]; } return self; } // 外觀調整方法 -(void)layoutSubviews { // 調用父類中的相同方法 [super layoutSubviews]; // 再顯示子元素 // 將child1_移動到左下 CGRect newRect = child1_.frame; newRect.origin.x = 0; newRect.origin.y = self.frame.size.height - child1_.frame.size.height; child1_.frame = newRect; // 將child2_移動到右上 newRect = child2_.frame; newRect.origin.x = self.frame.size.width - child2_.frame.size.width; newRect.origin.y = 0; child2_.frame = newRect; } @end
在上述實例中定義UIView的子類LayoutTest,其中擁有兩個標簽子元素。這里我們重新編寫了layoutSubviews方法,其中完成了將一個標簽放置在左下角顯示,另一個標簽放置在右上角顯示的外觀調整。將此LayoutTest追加到畫面中后,將顯示如圖2-14所示的外觀效果。

圖2-14 由layoutSubviews方法實現的外觀調整
這里我們直接改變標簽子元素的位置與尺寸后,可以看外觀是否會自動調整。將圖2-14中的“CHILD1”標簽的frame屬性作一下改變后,圖2-15顯示外觀并沒有作出自動調整。

圖2-15 直接修改子元素的frame屬性后不進行外觀自動調整
像上述這樣直接改變子元素的位置與尺寸時,layoutSubviews方法并沒有被調用。如果要改變這種狀況,我們需要調用setNeedsLayout方法。當UIView中發生某些改變后,再調用setNeedsLayout方法時,將會通知UIView需要調用其layoutSubviews方法,這樣一來就完成了外觀的自動調整。具體的代碼如下所示。
// 取得child1_并使之尺寸變大 UIView* child1 =(UIView*)[label_.subviews objectAtIndex:0]; child1.frame = CGRectMake(0,0,160,160); child1.center = label_.center; // 此后,調用setNeedsLayout方法 // layoutSubviews方法將會被自動調用 [label_ setNeedsLayout];
經過上述修改后,layoutSubviews方法將會被自動調用,最終效果如圖2-16所示,“CHILD1”標簽將會移動到左下的位置。
另外,如果想在調用setNeedsLayout 方法后不用等待而自動調用layoutSubviews方法,我們可以調用layoutIfNeeded方法,以強制調用layoutSubviews方法。調用layoutIfNeeded方法后,如果當UIView處于必須重新布局的情況下,將直接調用layoutSubviews方法。

圖2-16 使用 setNeedsLayout 方法后的效果
2.4.2 子元素的自動尺寸調整
當UIView中包含有子元素的情況下,如果其autoresizeSubviews屬性為YES時,當UIView發生尺寸調整時,其子元素將自動調整到合適的位置。autoresizeSubviews屬性的默認值就是YES,但是UIView中還存在一個必須設置的項目,就是子元素的autoresizingMask屬性。autoresizingMask屬性默認為UIViewAutoresizingNone,并不直接進行自動尺寸調整。autoresizingMask屬性中可以設置6種不同的標志,可以使用OR邏輯運算符將6種標志都設置上。表2-7中羅列了autoresizingMask屬性設置不同值時自動尺寸調整的變化情況。
表2-7 autoresizingMask屬性值及其變化效果
續表
續表
關于autoresizingMask屬性的實例在第4.2.2節有進一步的介紹,有興趣的讀者可以先行學習。
2.4.3 坐標變換
使用坐標時,有時取以特定對象為參照的相對坐標會更方便一些。例如,在[標簽1右側50像素處配置標簽2]的情況下。以標簽1的本地坐標為參照,標簽2的x坐標為[標簽1的寬度+50],這樣坐標的計算將變得更簡單。如果以父元素為參照,標簽2的x坐標將為[標簽1的x坐標+標簽1的寬度+50]。
用于坐標系變換的方法有:covertPoint:toView:方法、convertPoint:fromView:方法、convertRect:toView:方法、convertRect:fromView:方法四個。作用分別如表2-8所示。
表2-8 坐標系變換方法列表
下面看一個具體的使用實例。如圖2-17所示,畫面中有“CHILD1”及“CHILD2”兩個標簽,分別以“CHILD1”及“CHILD2”的本地坐標系變換對方的坐標。

圖2-17 兩個標簽的相對坐標(長寬均為100像素)
首先,將“CHILD1”的本地坐標系中“CHILD1”的右下(100,100)的坐標,以及“CHILD1”的矩形(0,0,100,100)變換到“CHILD2”的本地坐標系的坐標變換代碼如下。
CGPoint newPoint = [child1_ convertPoint:CGPointMake(100,100)toView:child2_]; CGRect newFrame = [child1_ convertRect:CGRectMake(0,0,100,100)toView:child2_];
結果為,newPoint的坐標為(0,0),newFrame的坐標為(-100,-100,100,100)。
接著,將“CHILD2”的本地坐標系中“CHILD2”的左上(0,0)的坐標,以及“CHILD2”的矩形(0,0,100,100)變換到“CHILD1”的本地坐標系的坐標變換代碼如下。
CGPoint newPoint = [child1_ convertPoint:CGPointMake(0,0)fromView:child2_]; CGRect newFrame = [child1_ convertRect:CGRectMake(0,0,100,100)fromView:child2_];
變換結果為,newPoint的坐標為(100,100),newFrame的坐標為(100,100,100,100)。