iOS動畫——Layer Animations(三)

我胡漢三又回來啦,忙過了前一段時間以后,今天又給大家帶來了兩個動畫,一個是形變、一個是顏色漸變。如下所示:


動畫來源《iOS Animation by tutorials》

源碼在github,地址在本文最后。


第一個動畫:
我們來分析一下第一個動畫,兩個頭像的位移我們前面已經講過了,今天主要講講形變,在這個動畫里一個是變橢圓,一個是變回正方形。
這兩個忍者(這里以及后面都把他稱呼為忍者)并不是UIImageView,而是一個UIView,然后在UIView中添加了UIImage(事實上用UIImageView也可以)。
我們來看一下他的層次結構:

事實上有三層哦

那么我們要做的第一步就是吧這三個圖層添加到UIView:
<pre><code>

let photoLayer         = CALayer()
let circleLayer        = CAShapeLayer()
let maskLayer          = CAShapeLayer()

/**
將Layer添加到父類
*/
override func didMoveToWindow() {
layer.addSublayer(photoLayer)
photoLayer.mask = maskLayer //遮罩層
layer.addSublayer(circleLayer)

}
/**
初始化三個Layer
*/
override func layoutSubviews() {
    
    //Size the avatar image to fit
    photoLayer.frame = CGRect(
        x: (bounds.size.width - image.size.width + lineWidth)/2,
        y: (bounds.size.height - image.size.height - lineWidth)/2,
        width: image.size.width,
        height: image.size.height)
    
    //Draw the circle
    circleLayer.path = UIBezierPath(ovalInRect: bounds).CGPath
    circleLayer.strokeColor = UIColor.whiteColor().CGColor
    circleLayer.lineWidth = lineWidth
    circleLayer.fillColor = UIColor.clearColor().CGColor
    
    //Size the layer
    maskLayer.path = circleLayer.path
    maskLayer.position = CGPoint(x: 0.0, y: 10.0)
    
}

</code></pre>

初始化部分:
我們聲明了三個CALayer,CAShapeLayer繼承自CALayer,然而這個layer可以用** UIBezierPath畫出你想要的圖形。
我們可以看到photoLayer聲明為了一個正方形、
circleLayer則用UIBezierPath畫了一個圓圈,并且設置了線條的顏色和線條的寬度,同時把填充顏色設置為clearColor。
maskLayer也是一個圓,但是我們同時設置他的
positon屬性,講他向下挪了10個point,注意position的坐標遠點為左上角。
設置部分:
我們將
maskLayerphotoLayer添加到了UIView上,前面我們說過了photoLayer**是一個正方形,那么他是如何顯示為圓形的呢,我們給他設置了一個遮罩層:

   photoLayer.mask = maskLayer        //遮罩層

寫過flash的同學一定知道遮罩層的概念是什么,遮罩層就是顯示的部分,如上設置以后,photoLayer就只顯示maskLayer的部分,所以當maskLayer發生改變的時候,photoLayer顯示的部分也會發生改變,所以我們接下來只需要對maskLayer進行動畫設置就可以。

我們現在運行一下程序看一下效果,你可以看到是這樣的:

1.png

好的接下來就是關鍵的動畫代碼了,如下:

<pre><code>
func bounceOffPoint(bouncePoint: CGPoint, morphSize: CGSize) {
let originalCenter = center

    UIView.animateWithDuration(animationDuration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.0, options: nil, animations: {
        
        self.center = bouncePoint
        
        }, completion: {_ in
            //complete bounce to
            if self.shouldTransitionToFinishedState {
                self.animateToSquare()
            }
    })
    
    UIView.animateWithDuration(animationDuration, delay: animationDuration, usingSpringWithDamping: 0.7, initialSpringVelocity: 1.0, options: nil, animations: {
        self.center = originalCenter
        }, completion: {_ in
            delay(seconds: 0.1) {
                if !self.isSquare {
                    self.bounceOffPoint(bouncePoint, morphSize: morphSize)
                }
            }
    })

/// 橢圓動畫

    let morphedFrame = (originalCenter.x > bouncePoint.x) ?
        
        CGRect(x: 0.0, y: bounds.height - morphSize.height,
            width: morphSize.width, height: morphSize.height):
        
        CGRect(x: bounds.width - morphSize.width,
            y: bounds.height - morphSize.height,
            width: morphSize.width, height: morphSize.height)
    
    let morphAnimation = CABasicAnimation(keyPath: "path")
    morphAnimation.duration = animationDuration
    morphAnimation.toValue = UIBezierPath(ovalInRect: morphedFrame).CGPath
    morphAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
    
    circleLayer.addAnimation(morphAnimation, forKey:nil)
    maskLayer.addAnimation(morphAnimation, forKey: nil)
}
/**
方形動畫
*/
func animateToSquare() {
    isSquare = true
    
    let squarePath = UIBezierPath(rect: self.bounds).CGPath
    let morph = CABasicAnimation(keyPath: "path")
    
    morph.duration = 0.25
    morph.fromValue = circleLayer.path
    morph.toValue = squarePath
    
    circleLayer.addAnimation(morph, forKey: nil)
    maskLayer.addAnimation(morph, forKey: nil)
    
    circleLayer.path = squarePath
    maskLayer.path = squarePath
}

</code></pre>

我們主要看橢圓動畫部分和方形動畫部分。
我們在這里可以看到我們還是創建了一個CALayer Aniamtion CABasicAnimation,這個動畫在前面部分我們已經看到很多回了,但是在這里我們的keypath為“path”確實第一次做,當keypath為“path”的時候主要是對UIView的形狀進行改變。
我們可以看到這個** CABasicAnimation的結構和之前還是一樣的,同樣是.toValue.fromValue.duration。所以當對形狀進行動畫的時候關鍵就是如何用 UIBezierPath**來描繪你想要編程的圖像。
我們今天要描繪的是兩個非常簡單的圖案。
<pre><code>
morphAnimation.toValue = UIBezierPath(ovalInRect: morphedFrame).CGPath //描繪了一個橢圓
let squarePath = UIBezierPath(rect: self.bounds).CGPath //描繪了一個正方形
</code></pre>

UIBezierPath給我們定制很多非常方便的初始化函數,更多信息請查看官方文檔。

中間還有一些時間的計算和距離的計算這里就不多進行講述,相信大家能自己看懂。


來看第二個動畫

第二個動畫就是一個漸變的色的改變,在這里我們需要了解一個類CAGradientLayer,這個圖層很好的實現了對顏色的控制。
他有幾個屬性需要了解,startPointendPointcolorslocations,我們來看一下我們這里的對CAGradientLayer的初始化過程
<pre><code>
let gradientLayer: CAGradientLayer = {
let gradientLayer = CAGradientLayer()

// Configure the gradient here

gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)

let colors = [
  UIColor.yellowColor().CGColor,
  UIColor.greenColor().CGColor,
  UIColor.orangeColor().CGColor,
  UIColor.cyanColor().CGColor,
  UIColor.redColor().CGColor,
  UIColor.yellowColor().CGColor

]
gradientLayer.colors = colors

let locations = [
  0.0, 0.0, 0.0, 0.0, 0.0, 0.25
]
gradientLayer.locations = locations

return gradientLayer
}()

</code></pre>

這里需要注意的就是漸變方向默認是由上到下的,而當我們設施startPointendPoint以后可以改變漸變的方向。locations對應每一個colors中的color的位置。然后我們將這個CALayer添加到UIView。
<pre><code>
override func layoutSubviews() {

gradientLayer.frame = CGRect(
  x: -bounds.size.width,
  y: bounds.origin.y,
  width: 3 * bounds.size.width,
  height: bounds.size.height)

}

override func didMoveToWindow() {

super.didMoveToWindow()

layer.addSublayer(gradientLayer)

}
</code></pre>

當然我們必須記得給這個CALayer設置frame,不然默認為0,我們現在可以運行看一下效果:

1.png

我們已經實現了顏色的漸變,那么我們現在給他添加動畫:
<pre><code>
let gradientAnimation = CABasicAnimation(keyPath: "locations")
gradientAnimation.fromValue = [
0.0, 0.0, 0.0, 0.0, 0.0, 0.25
]
gradientAnimation.toValue = [
0.65, 0.8, 0.85, 0.9, 0.95, 1.0
]

gradientAnimation.duration    = 3.0
gradientAnimation.repeatCount = Float.infinity

gradientLayer.addAnimation(gradientAnimation, forKey: nil)

</code></pre>

我們這里的keypath為locations也就是改變每一個顏色的位置,那么就實現了動畫的過程。** CABasicAnimation**的框架和前面并沒有太大的區別,我們 再來運行一下程序看一下效果:

10.gif

效果如上所示,我們已經大概完成了動畫,顏色唰唰的滾動,咦,但是怎么好像和最前面看到的不大一樣的,開篇我們看到的明明是一串字符串啊。哦~我們還需要制作一個遮罩層,這個遮罩層是一個文字的遮罩層。
代碼如下:
<pre><code>
@IBInspectable var text: String! {
didSet {
UIGraphicsBeginImageContextWithOptions(frame.size, false, 0)
let context = UIGraphicsGetCurrentContext()
text.drawInRect(bounds, withAttributes: textAttributes)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

  let maskLayer             = CALayer()
  maskLayer.backgroundColor = UIColor.clearColor().CGColor
  maskLayer.frame           = CGRectOffset(bounds, bounds.size.width, 0)
  maskLayer.contents        = image.CGImage

    
  gradientLayer.mask        = maskLayer
}

}
</code></pre>

這里出現了很多沒有見過的類,乍一眼看過去,“娘的,嚇尿了~”,然而事實上并不復雜。
UIGraphicsBeginImageContextWithOptions用來創建一個context,他和** UIGraphicsEndImageContext是一對,在他們倆之間我們可以來繪制一些圖片,
** UIGraphicsGetCurrentContext
創建了一個棧,

text.drawInRect(bounds, withAttributes: textAttributes)

這句話將文字繪制成圖片,并壓入上面我們創建的那一個棧,

 let image                 = UIGraphicsGetImageFromCurrentImageContext()

這句話取棧頂,也就是我們之前的用text繪制的圖片,那么現在image就是我們之前的文字,只不過是UIImage格式的。你可以理解為PNG格式~(不知道我這么理解對不還是不對,若是有大神看到請指出。)

接下來設置遮罩層和上一個動畫就沒有太大的區別的了;

好了 好了,我們終于寫完了這兩個動畫,這兩個動畫并不復雜,結合代碼和我的解釋 大家都應該能夠看懂。


代碼已上傳github:https://github.com/superxlx/iOS_Animation_Test5

覺得本文對你有幫助的請關注我,并點擊一下喜歡~親~

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

推薦閱讀更多精彩內容