不熟悉幾何學的人就不要來這裏了 --柏拉圖學院入口的簽名 git
在第二章裏面,咱們介紹了圖層背後的圖片,和一些控制圖層座標和旋轉的屬性。在這一章中,咱們將要看一看圖層內部是如何根據父圖層和兄弟圖層來控制位置和尺寸的。另外咱們也會涉及如何管理圖層的幾何結構,以及它是如何被自動調整和自動佈局影響的。 github
UIView有三個比較重要的佈局屬性:frame,bounds和center,CALayer對應地叫作frame,bounds和position。爲了能清楚區分,圖層用了「position」,視圖用了「center」,可是他們都表明一樣的值。 佈局
frame表明了圖層的外部座標(也就是在父圖層上佔據的空間),bounds是內部座標({0, 0}一般是圖層的左上角),center和position都表明了相對於父圖層anchorPoint所在的位置。anchorPoint的屬性將會在後續介紹到,如今把它想成圖層的中心點就行了。圖3.1顯示了這些屬性是如何相互依賴的。 ui
圖3.1 UIView和CALayer的座標系 atom
視圖的frame,bounds和center屬性僅僅是存取方法,當操縱視圖的frame,其實是在改變位於視圖下方CALayer的frame,不可以獨立於圖層以外改變視圖的frame。 spa
對於視圖或者圖層來講,frame並非一個很是清晰的屬性,它實際上是一個虛擬屬性,是根據bounds,position和transform計算而來,因此當其中任何一個值發生改變,frame都會變化。相反,改變frame的值一樣會影響到他們當中的值 指針
記住當對圖層作變換的時候,好比旋轉或者縮放,frame實際上表明瞭覆蓋在圖層旋轉以後的整個軸對齊的矩形區域,也就是說frame的寬高可能和bounds的寬高再也不一致了(圖3.2) component
圖3.2 旋轉一個視圖或者圖層以後的frame屬性 orm
以前提到過,視圖的center屬性和圖層的position屬性都指定了anchorPoint相對於父圖層的位置。圖層的anchorPoint經過position來控制它的frame的位置,你能夠認爲anchorPoint是用來移動圖層的把柄。 接口
默認來講,anchorPoint位於圖層的中點,因此圖層的將會以這個點爲中心放置。anchorPoint屬性並無被UIView接口暴露出來,這也是視圖的position屬性被叫作「center」的緣由。可是圖層的anchorPoint能夠被移動,好比你能夠把它置於圖層frame的左上角,因而圖層的內容將會向右下角的position方向移動(圖3.3),而不是居中了。
圖3.3 改變anchorPoint的效果
和第二章提到的contentsRect和contentsCenter屬性相似,anchorPoint用單位座標來描述,也就是圖層的相對座標,圖層左上角是{0, 0},右下角是{1, 1},所以默認座標是{0.5, 0.5}。anchorPoint能夠經過指定x和y值小於0或者大於1,使它放置在圖層範圍以外。
注意在圖3.3中,當改變了anchorPoint,position屬性保持固定的值並無發生改變,可是frame卻移動了。
那在什麼場合須要改變anchorPoint呢?既然咱們能夠隨意改變圖層位置,那改變anchorPoint不會形成困惑麼?爲了舉例說明,咱們來舉一個實用的例子,建立一個模擬鬧鐘的項目。
鐘面和鐘錶由四張圖片組成(圖3.4),爲了簡單說明,咱們仍是用傳統的方式來裝載和加載圖片,使用四個UIImageView實例(固然你也能夠用正常的視圖,設置他們圖層的contents圖片)。
圖3.4 組成鐘面和鐘錶的四張圖片
鬧鐘的組件經過IB來排列(圖3.5),這些圖片視圖嵌套在一個容器視圖以內,而且自動調整和自動佈局都被禁用了。這是由於自動調整會影響到視圖的frame,而根據圖3.2的演示,當視圖旋轉的時候,frame是會發生改變的,這將會致使一些佈局上的失靈。
咱們用NSTimer來更新鬧鐘,使用視圖的transform屬性來旋轉鐘錶(若是你對這個屬性不太熟悉,不要着急,咱們將會在第5章「變換」當中詳細說明),具體代碼見清單3.1
圖3.5 在Interface Builder中佈局鬧鐘視圖
清單3.1 Clock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIImageView *hourHand;
@property (nonatomic, weak) IBOutlet UIImageView *minuteHand;
@property (nonatomic, weak) IBOutlet UIImageView *secondHand;
@property (nonatomic, weak) NSTimer *timer;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//start timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];

//set initial hand positions
[self tick];
}
- (void)tick
{
//convert time to hours, minutes and seconds
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
CGFloat hoursAngle = (components.hour / 12.0) * M_PI * 2.0;
//calculate hour hand angle //calculate minute hand angle
CGFloat minsAngle = (components.minute / 60.0) * M_PI * 2.0;
//calculate second hand angle
CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0;
//rotate hands
self.hourHand.transform = CGAffineTransformMakeRotation(hoursAngle);
self.minuteHand.transform = CGAffineTransformMakeRotation(minsAngle);
self.secondHand.transform = CGAffineTransformMakeRotation(secsAngle);
}
@end
|
運行項目,看起來有點奇怪(圖3.6),由於鐘錶的圖片在圍繞着中心旋轉,這並非咱們期待的一個支點。
圖3.6 鐘面,和不對齊的鐘指針
你也許會認爲能夠在Interface Builder當中調整指針圖片的位置來解決,但其實並不能達到目的,由於若是不放在鐘面中間的話,一樣不能正確的旋轉。
也許在圖片末尾添加一個透明空間也是個解決方案,但這樣會讓圖片變大,也會消耗更多的內存,這樣並不優雅。
更好的方案是使用anchorPoint屬性,咱們來在-viewDidLoad方法中添加幾行代碼來給每一個鐘指針的anchorPoint作一些平移(清單3.2),圖3.7顯示了正確的結果。
清單3.2
1
2
3
4
5
6
7
8
9
10
11
12
|
- (void)viewDidLoad
{
[super viewDidLoad];
// adjust anchor points
self.secondHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
self.minuteHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
self.hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
// start timer
}
|
圖3.7 鐘面,和正確對齊的鐘指針