輪盤猜大小(仿淘寶彩票App)

支持原創,原文地址:www.KentonYu.com

作品集,其實也談不上作品吧。都是一些小 Demo 。我覺得有必要把一些覺得有意思的東西放在這里展示一下。讓大家來吐槽一下。并不斷的改進。但是有一個傷透腦經的問題,App 開發并不好展示,特別是 iOS,當然有值得展示的上架應用就會把 App Store link 放上來,那些小 Demo ,就只能先上錄屏了。

錄屏播放地址

主要知識點


貝塞爾曲線

給定n+1個數據點,p0(x0 , y0) ... pn(xn , yn),生成一條曲線,使得該曲線與這些點所連結的折線相近。在數學中,這屬于逼近問題。在幾何中,可以形象地理解為先用折線段連接這些數據點,勾勒出圖形的大致輪廓,然后再用光滑的曲線去盡可能接近地擬合這條折線。摘錄來自: “A GUIDE TO IOS ANIMATION”

UIBezierPath,是 UIKit 對 CoreGraphics 的 path (CGPathRef)的封裝。通過 UIBezierPath 可以繪制直線、圓圈、多邊形和貝塞爾曲線。
下面是對 UIBezierPath 的簡單介紹

<pre>
// 以下是四個類方法,用來繪制閉合的特殊路徑
// 矩形

  • (UIBezierPath *)bezierPathWithRect:(CGRect)rect

// 圓角矩形

  • (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius

// 矩形內切圓

  • (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect

// 弧形(clockwise 是否順時針繪制)

  • (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
    </pre>
    <pre>
    // 實例方法,可以繪制各種自定義的形狀
    // 直線
  • (void)addLineToPoint:(CGPoint)point

// 弧形線段

  • (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise

// 二階貝塞爾曲線

  • (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint

// 三階貝塞爾曲線

  • (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
    </pre>

通過貝塞爾曲線可以繪制任何你想要畫的形狀。我還沒用過三階貝塞爾。這個<a target="_blank" style='color:#00d6cf'>網站</a>可以體驗下貝塞爾曲線。


CALayer

CALayer 類在概念上和 UIView 類似,同樣也是一些被層級關系樹管理的矩形塊,同樣也可以包含一些內容(圖片,文本或者背景色),管理子圖層的位置。它們有一些方法和屬性用來做動畫和變換。和 UIView 最大的不同是 CALayer 不處理用戶的交互( UIView 繼承 UIResponder,CALayer 繼承 NSObject )。


CALayer 和 UIView 主要區別:

  • 處理觸摸事件
  • 陰影、圓角、帶顏色的邊框
  • 3D 變換
  • 非矩形范圍
  • 透明遮罩
  • 多級非線性動畫
  • .....


CALayer 的子類簡單介紹:

  • CAShapeLayer:用來繪制各種形狀
  • CATextLayer:用來繪制文字
  • CATransformLayer:用來構造一個層級的3D結構
  • CAGradientLayer:用來生成兩種或更多顏色平滑漸變
  • CAReplicatorLayer:用來高效地生成許多相似的圖層
  • CAScrollLayer:用來實現圖層滑動
  • CATiledLayer:為載入大圖造成的性能問題提供一個解決方案,將大圖分解成小片然后將它們單獨按需載入
  • CAEmitterLayer:高性能的粒子引擎,被用來創建實時粒子動畫(煙、火、雨等)
  • CAEAGLLayer:The CAEAGLLayer class supports drawing OpenGL content in iPhone applications
  • AVPlayerLayer:用來在iOS上播放視頻的,是 MPMoivePlayer 的底層實現,由 AVFoundation 提供

<b>關于這一部分的具體實踐,后續會補上。</b>


我們什么時候需要使用 CALayer:

  • 開發同時可以運行在 MAC OS 上的跨平臺應用
  • 使用多種 CALayer 的子類
  • 做一些對性能要求很高的工作


drawRect && drawLayer

CALayer 有一個寄宿圖,可以通過 contents 屬性來賦值:
<pre>
layer.contents = (__bridge id)image.CGImage;
</pre>

當用代碼的方式來處理寄宿圖的時候,一定要記住要手動的設置圖層的contentsScale屬性,否則,你的圖片在Retina設備上就顯示得不正確啦。代碼如下:
<pre>
layer.contentsScale = [UIScreen mainScreen].scale;
</pre>

以上是對寄宿圖的簡單介紹,下面就介紹主題 <code>- drawRect:</code>。
給 contents 賦值(CGImage)并不是唯一設置寄宿圖的方法,我們也可以通過用 Core Graphics 直接繪制寄宿圖,能夠通過繼承 UIView 并實現<code>- drawRect:</code>來自定義繪制。

<code>- drawRect:</code> 方法沒有默認實現,因為對 UIView 來說,寄宿圖并不是必須的。如果 UIView 檢測到 <code>- drawRect:</code> 方法被調用了,它就會為視圖分配一個寄宿圖,這個寄宿圖的大小的尺寸等于視圖大小乘以 contentScale 的積。

<b>所以在不需要使用寄宿圖的時候,就不要創建<code>- drawRect:</code>方法,會導致 CPU 和內存資源的浪費,因此在沒有自定義繪制任務的 UIView 子類中不要寫一個空的<code>- drawRect:</code>方法</b>

在視圖顯示到屏幕上時,<code>- drawRect:</code>會被自動調用,并且當一些表現效果的屬性值被修改時,一些視圖類型也會被重繪(如 bounds 屬性)。雖然<code>- drawRect:</code>是 UIView 的方法,但是還是底層的 CALayer 進行了重繪的操作并保存了產生的寄宿圖。

CALayer 中設置寄宿圖的過程:首先 CALayer 會請求它的 CALayerDelegate 代理給它一個寄宿圖來顯示。它通過下面這個方法來實現獲取。
<pre>

  • (void)displayLayer:(CALayerCALayer *)layer;
    </pre>

如果代理不實現這個方法, CALayer 會轉而嘗試調用下面這個方法:
<pre>

  • (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
    </pre>

在調用這個方法之前,CALayer 創建了一個合適尺寸的空寄宿圖(尺寸由 bounds 和 contentsScale 決定)和一個 CoreGraphics 的繪制上下文環境,為繪制寄宿圖做準備,他作為 ctx 參數傳入。

但是在使用這個方法時,不同于 UIView ,CALayer 不會自動重繪它的內容,需要手動調用 <code>- display</code>。

總的來說,當使用寄宿了視圖的圖層的時候,不必實現<code>- displayLayer:</code>和<code>- drawLayer: inContext:</code>方法來繪制寄宿圖。通常做法是實現 UIView 的<code>- drawRect:</code>方法,UIView 會做完剩下的工作,包括在需要重繪的時候調用<code>- display</code>方法。

這是一篇關于 drawRect 的博文《內存惡鬼 drawRect》 ,看完可以學到更多關于 drawRect 的知識。

NSTimer && CADisplayLink

NSTimer 是如何工作的?

當設置一個 NSTimer 時,NSTimer 會被插入到當前的 NSRunloop 中,然后直到指定的時間過去之后才會被執行。但是什么時候啟動定時器并沒有上限,而且只有當 NSRunloop 的上一個任務結束之后才會被執行,因此通常會導致不定時的延遲。 NSRunloop 主要爭對主線程,其中包含的任務有如下幾項:

  • 處理觸摸事件
  • 發送和接受網絡數據包
  • 執行使用 GCD 的代碼
  • 處理計時器的行為
  • 屏幕重繪

因此屏幕重繪的頻率是60次/秒,但是和定時器一樣,如果上一次重繪執行很長的時間,那么也會導致延遲。就不能保證定時器精準的每一秒執行60次。

對于動畫的實現可以通過以下這些途徑進行優化:

  • 可以用 CADisplayLink 讓更新頻率嚴格控制在每次屏幕刷新之后
  • 基于真實幀的持續時間而不是假設的更新頻率來做動畫
  • 調整動畫計時器的 RunLoop 模式,這樣就不會被別的事件干擾

用 CADisplayLink 而不是 NSTimer,會保證幀率足夠連續,使得動畫看起來更加平滑,但即使 CADisplayLink 也不能保證每一幀都按計劃執行,一些失去控制的離散的任務或者事件(例如資源緊張的后臺程序)可能會導致動畫偶爾地丟幀。

無論是使用 NSTimer 還是 CADisplayLink,我們仍然需要處理一幀的時間超出了預期的1/60秒。由于我們不能夠計算出一幀真實的持續時間,所以需要手動測量。我們可以在每幀開始刷新的時候用 CACurrentMediaTime() 記錄當前時間,然后和上一幀記錄的時間去比較。


總結

在這個 Demo 里主要涉及了以上幾個知識點,通過 CAShapeLayer 來繪制整個臺子,然后通過實現 <code>- drawLayer: inContext:</code> 實現橢圓進度條的效果(在淘寶彩排H5里有個顏色漸變的過程,我沒有實現)。通過這個小 Demo,鞏固了之前 iOS 動畫學習的知識,感覺寫動畫是一個很有趣的事情。當然不得不說,這個看似簡單的小游戲,實際做起來邏輯還是有點復雜的~

相關資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,573評論 6 30
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,147評論 5 13
  • 書寫的很好,翻譯的也棒!感謝譯者,感謝感謝! iOS-Core-Animation-Advanced-Techni...
    錢噓噓閱讀 2,327評論 0 6
  • 文/婉兮 1 和王瀚的這場相見蓄謀已久,下定決心卻只是三天前。 那晚詩雅穿了一條新裙子,花蝴蝶一般在各個房間穿梭,...
    婉xi閱讀 6,775評論 46 179
  • 晚飯后,我獨自走在市鎮廣場。雖然已經是秋天,晚風卻是悶熱的。路過昏黃的路燈,身影逐漸被拉長,而后消失不見。突然感覺...
    重生之夏閱讀 181評論 0 1