frame
frame的官方解釋如下:
The frame rectangle, which describes the view’s location and size in its superview’s coordinate system.
This rectangle defines the size and position of the view in its superview’s coordinate system. Use this rectangle during layout operations to set the size and position the view. Setting this property changes the point specified by the center property and changes the size in the bounds rectangle accordingly. The coordinates of the frame rectangle are always specified in points.
它定義了一個view相對于父視圖坐標(biāo)系的位置和大小漫萄,它會影響center屬性和bounds屬性的size腾务。
先看一下它究竟是什么岩瘦?
它是一個CGRect類型,如下:
struct?CGRect?{?
????????CGPoint?origin;????
????????CGSize?size;
};
typedef?struct?CG_BOXABLE?CGRect?CGRect;struct?CGPoint?{????
????????CGFloat?x;????
????????CGFloat?y;
};
typedef?struct?CG_BOXABLE?CGPoint?CGPoint;/*?Sizes.?*/struct?CGSize?{???
????????CGFloat?width;????
????????CGFloat?height;
};
typedef?struct?CG_BOXABLE?CGSize?CGSize;
其中的origin就是該view的位置启昧,它是一個CGPoint類型劈伴,也是一個結(jié)構(gòu)體,包含了我們熟知的常用二維坐標(biāo)系的x新啼、y燥撞。根據(jù)x迷帜、y可以在坐標(biāo)系里面唯一確定一個點瞬矩。如下圖:
這個坐標(biāo)系和我們平時接觸的還不太一樣景用,它是向右向下為正方向伞插。所以對于window來說媚污,其原點是左上角,比如現(xiàn)在的頭像的起始坐標(biāo)就是(200京髓,40)堰怨。按照原來常規(guī)的坐標(biāo)系來說备图,應(yīng)該是(200赶袄,-40)饿肺。
在設(shè)置一個CGRect的時候唬格,用到的方法是CGRectMake,其實現(xiàn)如下:
CG_INLINE?CGRect
CGRectMake(CGFloat?x,?CGFloat?y,?CGFloat?width,?CGFloat?height){
????????CGRect?rect;?
????????rect.origin.x?=?x;?
????????rect.origin.y?=?y;??
????????rect.size.width?=?width;?
????????rect.size.height?=?height;??
????????returnrect;
}
也就是自己在實現(xiàn)部分創(chuàng)建了一個rect购岗,然后逐個賦值喊积。
關(guān)于frame,這里要注意的一點就是:frame是相對于父視圖的坐標(biāo)系來定位的髓梅。如果你這樣設(shè)置frame:(0,0,100,200),也就是在父視圖左上角添加了一個寬100枯饿,高200的子視圖(前提是沒有改變父視圖的bounds奢方,接下來會有介紹bounds)蟋字。
bounds
The bounds rectangle, which describes the view’s location and size in its own coordinate system.
The default bounds origin is (0,0) and the size is the same as the size of the rectangle in the frame property. Changing the size portion of this rectangle grows or shrinks the view relative to its center point. Changing the size also changes the size of the rectangle in the frame property to match. The coordinates of the bounds rectangle are always specified in points.
Changing the bounds rectangle automatically redisplays the view without calling its drawRect: method. If you want UIKit to call the drawRect: method, set the contentMode property to UIViewContentModeRedraw.
Changes to this property can be animated.
它也是描述的是視圖的位置和大小鹊奖,只不過是在自己的坐標(biāo)系上忠聚。也就是說它描述的是當(dāng)前視圖相對于自身坐標(biāo)系的位置和大小唱捣。
舉個例子:
輸出的結(jié)果如下:
frame:{{60,?80},?{200,?408}}
bounds:{{0,?0},?{200,?408}}
center:{160,?284}
由此可見垫竞,如果我們沒有去更改bounds的值欢瞪,它默認的位置坐標(biāo)點是(0,0)徐裸。
center
The center point of the view's frame rectangle.
The center point is specified in points in the coordinate system of its superview. Setting this property updates the origin of the rectangle in the frame property appropriately.
Use this property, instead of the frame property, when you want to change the position of a view. The center point is always valid, even when scaling or rotation factors are applied to the view's transform.
Changes to this property can be animated.
center是view的中點骑祟。該屬性是想歸于父類的坐標(biāo)系確定的。從bounds小節(jié)里面的例子可以看到center的值怯晕,其計算方法為:
center.x = frame.origin.x + frame.size.width/2?
center.y = frame.origin.y + frame.size.height/2
transform
Specifies the transform applied to the view, relative to the center of its bounds.
Use this property to scale or rotate the view's frame rectangle within its superview's coordinate system. (To change the position of the view, modify the center property instead.) The default value of this property is CGAffineTransformIdentity.
Transformations occur relative to the view's anchor point. By default, the anchor point is equal to the center point of the frame rectangle. To change the anchor point, modify the anchorPoint property of the view's underlying CALayer object.
Changes to this property can be animated.
In iOS 8.0 and later, the transform property does not affect Auto Layout. Auto layout calculates a view’s alignment rectangle based on its untransformed frame.
它用于指定視圖的變換。使用這個屬性可以放大或者旋轉(zhuǎn)視圖堵第,它的frame會因此改變踏志,是以中心點為變換的针余『桑看例子:
看輸出的結(jié)果:
frame:{{60,?80},?{200,?408}}
bounds:{{0,?0},?{200,?408}}
center:{160,?284}
after?change?transform,frame:{{2.5773352536321568,?59.226689885086444},?{314.84532949273569,?449.54662022982711}}
after?change?transform,bounds:{{0,?0},?{200,?408}}
after?change?transform,center:{160,?284}
如圖:
可以看出颤练,當(dāng)我們對圖像通過旋轉(zhuǎn),旋轉(zhuǎn)后的圖片的frame已經(jīng)變成了{(2.5773352536321568, 59.226689885086444), (314.84532949273569, 449.54662022982711)},此時的起始位置為圖上旋轉(zhuǎn)后標(biāo)的(2.58,59.2),大小也變成了雙箭頭黑線標(biāo)注的大小。
因此得出結(jié)論:進行了transform變換驱负,其frame改變了嗦玖,但是其bounds和center并沒有修改。此時bounds的size和frame的size已經(jīng)沒有關(guān)系了跃脊。當(dāng)沒有進行任何transform時宇挫,frame的size總是和bounds相等。
以上便是對frame酪术、bounds器瘪、center和transform做了一個簡單的介紹。
bounds的使用
接下來看一個例子(例子A):
這里在parentView上添加了一個childView,然后對parentView的bounds進行修改和不修改進行了測試橡疼,結(jié)果如下:
你會發(fā)現(xiàn)當(dāng)修改了parentView的bounds之后,發(fā)現(xiàn)childView缺向右向下做了偏移庐舟。這里設(shè)置parentView的bounds的origin為(-40,-40)為何會發(fā)生這種情況呢欣除?接下來先看一下下面這張圖:
+代表正方向,-代表負方向挪略。
如果此時我們沒有改變圖中O的坐標(biāo)历帚,那么此時A的坐標(biāo)是(20,20)滔岳,如果我們更改了O的坐標(biāo)為(-20,-20),那么原來A點的坐標(biāo)就成了A'(0,0),但是A坐標(biāo)是不變的挽牢,所以它會到黑色A處谱煤。所以你改變了原點坐標(biāo)為負之后,A點會移動到黑色A卓研。相反如果你設(shè)置了坐標(biāo)原點為(20,20)趴俘,那么A點就會和坐標(biāo)原點重合睹簇。
這就是為什么childView會向右向下移動的原因奏赘。
接下來再做如下操作(例子B):
輸出結(jié)果如下:
parent?change?bound?,frame:{{60,?80},?{200,?408}}
parent?change?bound?,bounds:{{0,?0},?{200,?408}}
parent?change?bound?,center:{160,?284}
childView?frame:{{60,?288},?{100,?100}}
childView?ounds:{{0,?0},?{100,?100}}
childView?center:{110,?338}
--------
anim?finished,parentView?frame:{{60,?80},?{200,?408}}
anim?finished,parentView?ounds:{{0,?400},?{200,?408}}
anim?finished,parentView?center:{160,?284}
anim?finished,childView?frame:{{60,?288},?{100,?100}}
anim?finished,childView?bounds:{{0,?0},?{100,?100}}
anim?finished,childView?center:{110,?338}
運行效果是childView向上移動,然后停止太惠。結(jié)果前后對比圖如下:
直觀來看磨淌,按說childView的frame改變了,但是從console輸出的結(jié)果來看凿渊,childView的frame/bounds/center都沒有改變梁只,但是直觀來看其位置卻改變了。再看一下parentView,只有bounds改變了埃脏,frame和center卻沒變搪锣,從直觀來看parentView沒有任何更改。所以很有可能是parentView的bounds修改引起了childView的位置更改彩掐。這是為什么呢构舟?這里先不說明為什么,再看一下最常用的UIScrollView:
當(dāng)滾動視圖的時候堵幽,console輸出結(jié)果如下:
scrollview?frame:{{0,?0},?{320,?568}}
scrollview?bounds:{{0,?0},?{320,?568}}
scrollview?center:{160,?284}
parentView?frame:{{20,?100},?{250,?300}}
parentView?bounds:{{0,?0},?{250,?300}}
parentView?center:{145,?250}
didScroll?scrollview?frame:{{0,?0},?{320,?568}}
didScroll?scrollview?bounds:{{0,?-20},?{320,?568}}
didScroll?scrollview?center:{160,?284}
didScroll?parentView?frame:{{20,?100},?{250,?300}}
didScroll?parentView?bounds:{{0,?0},?{250,?300}}
didScroll?parentView?center:{145,?250}
============================
didScroll?scrollview?frame:{{0,?0},?{320,?568}}
didScroll?scrollview?bounds:{{8.5,?31.5},?{320,?568}}
didScroll?scrollview?center:{160,?284}
didScroll?parentView?frame:{{20,?100},?{250,?300}}
didScroll?parentView?bounds:{{0,?0},?{250,?300}}
didScroll?parentView?center:{145,?250}
============================
didScroll?scrollview?frame:{{0,?0},?{320,?568}}
didScroll?scrollview?bounds:{{25.5,?162},?{320,?568}}
didScroll?scrollview?center:{160,?284}
didScroll?parentView?frame:{{20,?100},?{250,?300}}
didScroll?parentView?bounds:{{0,?0},?{250,?300}}
didScroll?parentView?center:{145,?250}
根據(jù)輸出結(jié)果可以看到狗超,parentView的center、frame朴下、bounds在滾動過程中都沒有作出更改努咐,但是我們看到的它的位置的確改變了。而對于scrollView來說殴胧,其frame和center也沒有更改渗稍,但是bounds更改了。
這種現(xiàn)象和上面提到的(例子B)的現(xiàn)象一樣团滥,都是對bounds進行了修改竿屹。然后子視圖從新進行了布局。說道子視圖重新布局惫撰,讓我想到了一個方法:
1-?(void)layoutSubviews;
從字面意思看就是布局某個視圖的子視圖羔沙,那么會不會和這個方法有關(guān)呢?因此我在自定義的ZGUIScrollView里面實現(xiàn)了該方法:
- (void)layoutSubviews {? ??
????????print(@"scrollview's layoutSubViews called")
????????super.layoutSubviews
}
再次滾動界面厨钻,發(fā)現(xiàn)每次滾動都會調(diào)用scrollview的layoutSubViews方法扼雏。蘋果官方文檔介紹:
Lays out subviews.
The default implementation of this method does nothing on iOS 5.1 and earlier. Otherwise, the default implementation uses any constraints you have set to determine the size and position of any subviews.
Subclasses can override this method as needed to perform more precise layout of their subviews. You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.
You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
它的作用就是布局一個視圖上的子視圖坚嗜。確定子視圖的大小和位置。如果你想強制布局更新诗充,你不能直接去調(diào)用這個方法苍蔬,而是在下次更新圖形之前調(diào)用setNeedsLayout方法,如果你要立即更新視圖布局蝴蜓,調(diào)用layoutIfNeeded方法碟绑。
由此可知,UIScrollView的實現(xiàn)就是通過bounds來實現(xiàn)的茎匠。contentOffset是bounds的origin格仲。然后當(dāng)bounds修改之后,會在layoutSubviews方法里面對子視圖進行布局诵冒。對子類進行更新凯肋。
另外,我們還可以用bounds實現(xiàn)如下效果:
圖上右側(cè)便是使用了bounds實現(xiàn)的效果汽馋。實現(xiàn)方式就是在自定義cell中重寫drawReact:-?(void)drawRect:(CGRect)rect?{?????
????????self.bounds?=?CGRect(self.bounds.origin.x,?self.bounds.origin.y, ? ? ? ? self.frame.width-20,?self.frame.height?-?5)
????????super.drawRect(rect)
}
其實UITableView(它是UIScrollView)的實現(xiàn)也是類似侮东,更改了bounds,來實現(xiàn)滾動加載cell。
總結(jié)
對bounds和frame的理解就是這些豹芯,其實系統(tǒng)用bounds的地方還是很多的悄雅。例如UIScrollView的實現(xiàn)就用到了。有疑問的話可以留言交流铁蹈。
參考: