關(guān)于frame
- frame是一個(gè)復(fù)合屬性,由center、bounds和transform共同計(jì)算而來复局。
- transform改變,frame會(huì)受到影響粟判,但是center和bounds不會(huì)受到影響亿昏。也就是你使用transform來縮放,bounds是不會(huì)變的档礁。那么由center和bounds計(jì)算得到的frame是永遠(yuǎn)保持transform為
identity
時(shí)的狀態(tài)角钩。這也是為什么把transform設(shè)為identity
后,view會(huì)回歸到最初狀態(tài)。
關(guān)于transform的計(jì)算
當(dāng)你使用view.transform = xxx
時(shí)候递礼,它到底是怎么起作用的惨险?首先,它是一個(gè)矩陣脊髓,使用矩陣乘法辫愉,對view的frame進(jìn)行變換,得到新的變換将硝,那么這個(gè)邏輯是怎樣的恭朗?
- 它是針對父視圖坐標(biāo)的。
- 它是針對view的初始中心為坐標(biāo)的:“初始”是指transform值為
identity
時(shí)的狀態(tài)依疼,即沒有任何的縮放痰腮、平移或旋轉(zhuǎn);“中心”默認(rèn)是view方塊的中心律罢,但實(shí)際是anchorPoint
膀值。
那么viewA.transform = myTransform
這么一段代碼就等價(jià)于:
- 把父視圖坐標(biāo)系的原點(diǎn)移動(dòng)到view的中心,計(jì)算中心坐標(biāo)系的frame,得到frame1:
CGRect frame1 = CGRectMake(
-originalFrame2.size.width/2.0,
-originalFrame2.size.height/2.0,
originalFrame2.size.width,
originalFrame2.size.height);
以坐標(biāo)系改變后的frame(即centerFrame)計(jì)算弟翘,使用矩陣乘法應(yīng)用transform,得到frame2:
CGRect frame2 = CGRectApplyAffineTransform(frame2, myTransform)
再把結(jié)果轉(zhuǎn)回原父視圖坐標(biāo)系,得到frame3:
CGRect frame3 = CGRectMake(
frame2.origin.x + CGRectGetMidX(originalFrame),
frame2.origin.y + CGRectGetMidY(originalFrame),
frame2.size.width,
frame2.size.height);
這么做的好處便是在縮放的時(shí)候虫腋,是針對view當(dāng)前位置的,這樣view的原點(diǎn)不會(huì)改變稀余,也就是縮放只會(huì)產(chǎn)生縮放的效果悦冀,而不會(huì)產(chǎn)生平移。假設(shè)使用父視圖原點(diǎn)睛琳,frame為{10,20,100,100},縮放后變成{5,10,50,50},那么frame不僅變小了盒蟆,也和原點(diǎn)更近了。
怎么計(jì)算兩個(gè)frame之間的transform
給你一個(gè)view和一個(gè)目標(biāo)frame师骗,求一個(gè)transform历等,使得把這個(gè)transform給view后,view的frame等于目標(biāo)frame辟癌。
在處理動(dòng)畫的時(shí)候會(huì)用到寒屯。
因?yàn)榭s放會(huì)影響平移,而平移卻不會(huì)影響縮放黍少,所以先平移到中心和目標(biāo)frame一致寡夹,然后縮放。
平移的距離就是兩個(gè)center的差值厂置,縮放比例的就是兩個(gè)frame的邊長之比菩掏。
即:
-(CGAffineTransform)transformFromRect:(CGRect)fromRect toRect:(CGRect)toRect{
CGAffineTransform moveTrans = CGAffineTransformMakeTranslation(CGRectGetMidX(toRect) - CGRectGetMidX(fromRect), CGRectGetMidY(toRect) - CGRectGetMidY(fromRect));
CGAffineTransform scaleTrans = CGAffineTransformMakeScale(toRect.size.width / fromRect.size.width, toRect.size.height / fromRect.size.height);
//右邊先執(zhí)行
return CGAffineTransformConcat(scaleTrans, moveTrans);
}
為什么使用transform動(dòng)畫而不是設(shè)置frame?
如果transform里包含了旋轉(zhuǎn),那么計(jì)算出來的frame就沒有意義了昵济,因?yàn)閒rame總是描述一個(gè)“擺正的”方塊智绸,而旋轉(zhuǎn)后的方塊是沒法描述的野揪。
但對于只有平移和縮放,用上述邏輯是可以計(jì)算的瞧栗。我在做一個(gè)過場動(dòng)畫的時(shí)候用到了這個(gè)斯稳,動(dòng)畫是類似系統(tǒng)相冊那樣從一個(gè)小圖逐漸放大到全屏,所以你擁有的信息是一個(gè)起始的frame沼溜,以這個(gè)為開始動(dòng)畫平挑。
我嘗試了通過直接設(shè)置frame來執(zhí)行動(dòng)畫,但發(fā)現(xiàn)效果糟糕系草,因?yàn)閯?dòng)畫雖然有一個(gè)過程,但其實(shí)從動(dòng)畫一開始唆涝,frame就已經(jīng)修改了找都。如果直接設(shè)置frame,那么開始的時(shí)候廊酣,子視圖就會(huì)按變化后的frame來重新布局能耻,而不是跟隨父視圖一起慢慢變化。
動(dòng)畫是渲染呈現(xiàn)上的樣子亡驰,而實(shí)際的數(shù)值卻是另一種樣子晓猛,在core animation里有模型樹和呈現(xiàn)樹的區(qū)別。
舉個(gè)例子:
測試view是灰色凡辱,它有一個(gè)子視圖是紅色:
-(void)layoutSubviews{
innerView.frame = CGRectMake(10, 10, self.frame.size.width-20, self.frame.size.height-20);
}
內(nèi)部的view保持和父視圖10的邊距戒职。所以看第一個(gè)動(dòng)畫,在剛開始的時(shí)候透乾,紅色的view就變成了動(dòng)畫結(jié)束時(shí)的大小洪燥,而第二個(gè)動(dòng)畫使用transform變換,其實(shí)layoutSubviews
并沒有調(diào)用乳乌,但是卻得到了想要的效果捧韵。貌似transform只是影響了view的渲染,而且是影響了整個(gè)的子視圖數(shù)汉操,就像把這個(gè)view當(dāng)做一張圖片一樣縮小了再来,而內(nèi)部卻不需要重新布局。
使用transform效果更好磷瘤,那么就要從一個(gè)初始frame計(jì)算得到transform芒篷,使得賦值給view后,它就是到初始frame的位置膀斋。所以就有了上面的transform計(jì)算梭伐。