IOS之Metro實現,擁有windows磁帖效果(一 動畫效果的實現)
圖
所有轉出“博客園”的朋友請您注明出處:http://www.49028c.com/xiaobajiu/p/4084717.html
扁平化來襲,微軟的metro風格看起來很有科技感。于是想寫個IOS的metro控件。搞了一段時間,有了模樣。我的想法是metro和wp8的差不多,展示信息通過旋轉來展示,觸摸metro它會撬動或者吸彈。點擊metro會執行事件,長按磁帖又可以執行其他事件。該帖中將詳細展示我的設計思路希望能給初學者更多啟發和學習資源。
metro的結構:metro最外層是骨架層,該層的作用是站住位置,內部的大小變化和metro的拖拽都不會影響到外部的結構。骨架外是蒙板層和內容層。蒙板層在下方作用是metro主題色,內容層存放要展示的兩面視圖。值得注意的是蒙板層在不設置主題的情況下是隱藏的。
metro結構-大圖
那么直接進入最關心的地方,動畫。IOS的動畫是廉價的,不像Windows,其他平臺都像拿筆在畫,IOS就像打印機在刷圖。IOS上主要的繪圖相關的有Quartz2D,核心動畫等應付簡單的旋轉和伸縮的畫使用UIViewAnimation就足夠了,而且代碼簡短。旋轉的效果竟然只要一行: view.layer.transform= CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z) 這樣子就可以實現旋轉效果。下面是一段完整的磁帖旋轉代碼的截?。?/p>
/** * (PRivate)metro的旋轉動畫 */- (void)revolveMetro{ _isViewDleaying= NO; [UIView animateWithDuration:_turnTimes[_currentViewID] delay:0.0 options:UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionAllowUserInteraction animations:^{ _isAnimating= YES;//修改動畫狀態 _container.layer.transform= CATransform3DMakeRotation(M_PI_2, 1.0, 0.0, 0.0); if(!_maskingView.hidden) _maskingView.layer.transform= CATransform3DMakeRotation(M_PI_2, 1.0, 0.0, 0.0); } completion:^(BOOL finished) { ((UIView*)self.views[_currentViewID]).hidden= YES; ((UIView*)self.views[[self nextIndexNowNextView]]).hidden= NO; [UIView animateWithDuration:_turnTimes[_currentViewID] delay:0.0 options:UIViewAnimationOptionCurveEaSEOut animations:^{ _container.layer.transform= CATransform3DMakeRotation(- M_PI_4/2, 1.0, 0.0, 0.0); if(!_maskingView.hidden) _maskingView.layer.transform= CATransform3DMakeRotation(- M_PI_4/2, 1.0, 0.0, 0.0); } completion:^(BOOL finished) { [UIView animateWithDuration:_turnTimes[_currentViewID] delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{ _container.layer.transform= CATransform3DMakeRotation(0, 1.0, 0.0, 0.0); if(!_maskingView.hidden) _maskingView.layer.transform= CATransform3DMakeRotation(0, 1.0, 0.0, 0.0); } completion:^(BOOL finished) { _isAnimating= NO;//修改動畫狀態 }]; }]; }];}
一共組合了三次動畫,每個動畫都可以通過options選項設置很多有用的屬性。比如動畫時間曲線,簡單說就是動畫隨漸漸的快慢,它畫到坐標軸上就是各種曲線。在磁帖的旋轉中有切換圖片的這種情況,有很多方法可以做比如我沒有用的一個方法: [view exchangeSubviewAtIndex:0 withSubviewAtIndex:1]; 這個方法可以直接交換父圖層中子級索引0和1的兩個子圖層的位置。從而達到切換圖層的目的。我使用的是隱藏圖層的方法,目的是不想打亂圖層順序,方便找到某圖層。
除了metro的旋轉還有一個動畫效果就是metro被觸摸的時候的傾斜效果。通過觀察wp8,觸摸點離中心點越遠下沉力度越大。但是靠近中心區域又是下沉效果而不是傾斜效果。通過觀察還能發現到這個動畫效果的觸發應該是在觸摸到metro的時候觸發。那么這個動畫效果可以在 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 這個函數中執行。
那么還剩一個繁瑣的問題就是傾斜的計算的問題。諾以幾何中心為對稱軸來進行旋轉,只加以控制旋轉的角度即可實現在metro一條通過幾何中心的直線上觸摸時根據到幾何中心的距離來計算應該旋轉多少角度。而旋轉所繞對稱軸就是上一句所說的通過幾何中心的直線(觸摸點和幾何中心的連線)旋轉90度所得直線。
可以創建一個結構結構體來代表這種向量:
/** * 帶權值的向量 */struct MetroVector{ CGFloat x; CGFloat y; CGFloat power;//權值標明metro下沉力度<-[0.0,1.0]};typedef struct MetroVector MetroVector;
下一步是當觸摸metro時獲得觸摸點a。由a和中心o計算出一條向量V1,V1旋轉90度既是傾斜所需的對稱軸V2。直接將V2的x,y填入旋轉函數中即可。值得注意的是 view.layer.transform= CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)這個函數的x,y,z值的取值區間是[0.0,1.0]。關于力度我采用的是線段ao的長度,對比的是o到metro任意角的距離即對角線的半長。兩只相除得到一個0~1的數即可衡量力度。
計算向量V2的代碼:
/** * (private)計算metro點擊時 3D傾斜的對稱軸的向量 * * @param view 觸摸的視圖 * @param touchPoint 其中的觸摸點 */- (MetroVector)getVectorByTouchedView:(UIView*)view in:(CGPoint)touchPoint{ MetroVector vector; CGFloat width= view.frame.size.width; CGFloat height= view.frame.size.height; CGFloat diagonal= sqrt(width* width+ height* height);//對角線 vector.x= touchPoint.x- width/2;//以幾何中心為原點的向量 vector.y= touchPoint.y- height/2; CGFloat lenTouchBetweenCenter= sqrt(vector.x*vector.x+ vector.y* vector.y);//觸摸點到幾何中心的距離 CGFloat max= MAX(vector.x, vector.y);//取最大值,做除數,使得兩者最大值為1.0 CGFloat temp= vector.x;//交換x,y值,其中之一取反(表示將向量轉動90度,成為圖層的轉軸) vector.x= - vector.y/max; vector.y= temp/max; CGFloat len4maxPower= diagonal/2;//取對角線二分之一作為力度最大的衡量 vector.power= lenTouchBetweenCenter/ len4maxPower; return vector;}
有了V2那么傾斜就可以直接上代碼了
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [super touchesBegan:touches withEvent:event]; if(self.isAnimating) return;//允許摸,不允許再次動畫 CGPoint touchPos= [(UITouch*)[touches anyObject] locationInView:self]; //中心區域 CGSize viewSize= self.frame.size; CGRect centerRect= CGRectMake(viewSize.width/4, viewSize.height/4, viewSize.width/2, viewSize.height/2); if(CGRectContainsPoint(centerRect, touchPos)){//是否在中心區域,是則下沉動畫 [UIView animateWithDuration:macroTouchOneAnimationInterval/5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionAllowUserInteraction animations:^{ _isAnimating= YES; self.layer.transform= CATransform3DMakeScale(0.975, 0.975, 1.0); } completion:^(BOOL finished) { [UIView animateWithDuration:macroTouchOneAnimationInterval/2 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ self.layer.transform= CATransform3DMakeScale(1.0, 1.0, 1.0); } completion:^(BOOL finished) { _isAnimating= NO; //.. }]; }]; }else{//偏斜動畫 MetroVector vecor= [self getVectorByTouchedView:self in:touchPos]; [UIView animateWithDuration:macroTouchOneAnimationInterval delay:0.0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionAllowUserInteraction animations:^{ _isAnimating= YES; self.layer.transform= CATransform3DMakeRotation(randianFromAngle(30* vecor.power),vecor.x, vecor.y, 0.0);// } completion:^(BOOL finished) { [UIView animateWithDuration:macroTouchOneAnimationInterval/2 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ self.layer.transform= CATransform3DMakeRotation(0 ,vecor.x, vecor.y, 0.0); } completion:^(BOOL finished) { _isAnimating= NO; //.. }]; }]; }}
到目前為最為核心的動畫內容已經實現。還剩下觸摸事件的處理,和一堆所謂的業務邏輯和一些功能上的實現。樓主會在下一篇博客另外介紹。并賦上代碼下載。
如有錯誤請朋友指正,以免誤人子弟。
下一篇博文地址:http://www.49028c.com/xiaobajiu/p/4106663.html
新聞熱點
疑難解答