當(dāng)看到遮罩層類的動畫和碰撞或者擠壓這樣的視圖時,應(yīng)該涉及多個形狀視圖間的動畫通危。此時,應(yīng)該明白將有一個形狀圖層灌曙,應(yīng)該有calayershapelayer類進(jìn)行處理菊碟。calayershapelayer類繼承calayer,在這個層上將會繪制各種形狀的圖形在刺。
用一個頭像示意圖的分層效果圖
額外增加的知識點(diǎn):1.photoLayer.mask = maskLayer逆害,標(biāo)示的是將mask layer作為photo layer的遮罩層。
2.@IBInspectable和@IBDesignable的作用是讓storyboard控件圖片的顯示與代碼的設(shè)置保持一致
把這3個圖層添加到涂層上的核心代碼:
// 當(dāng)avatarview 對象呈現(xiàn)在視圖上的時候蚣驼,會調(diào)用didMoveToWindow方法魄幕。讓layer呈現(xiàn)到視圖上
override func didMoveToWindow() {
layer.addSublayer(photoLayer)
photoLayer.mask = maskLayer
layer.addSublayer(circleLayer)
addSubview(label)
}
//layout方法中可以知道每一個層都將是一個矩形
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)
//Size the label
label.frame = CGRect(x: 0.0, y: bounds.size.height + 10.0, width: bounds.size.width, height: 24.0)
}
截下來就要開始做動畫了,實現(xiàn)一個頭像發(fā)生碰撞的動畫
游戲開始的時候搜索對手的動畫核心代碼:
思路:頭像從原始位置到達(dá)碰撞點(diǎn)颖杏,當(dāng)碰撞的動畫完成之后纯陨,返回到原始的位置,接著在重復(fù)做上述動畫。
首先為在搜索對手需要準(zhǔn)備的偏移量翼抠,變形大小咙轩,形變寫入一個方法里,將它傳入頭像視圖里做動畫
func searchForOpponent(){
let avatarSize = myAvatar.frame.size
let bounceXOffset: CGFloat = avatarSize.width/1.9//設(shè)置一個水平方向上的反彈偏移量
let morphSize = CGSize(
width: avatarSize.width * 0.85,
height: avatarSize.height * 1.1)//設(shè)置了頭像的變形大小
//計算2個頭像到達(dá)反彈點(diǎn)時發(fā)生輕微的碰撞阴颖,計算出左右2個反彈點(diǎn)活喊,然后通過動畫將其分開
let rightBouncePoint = CGPoint(
x: view.frame.size.width/2.0 + bounceXOffset,
y: myAvatar.center.y)
let leftBouncePoint = CGPoint(
x: view.frame.size.width/2.0 - bounceXOffset,
y: myAvatar.center.y)
myAvatar.bounceOffPoint(rightBouncePoint, morphSize: morphSize)
/*第一個參數(shù)是頭像發(fā)生碰撞的點(diǎn),
第二個參數(shù)是頭像發(fā)生碰撞時的變形大小*/
opponentAvatar.bounceOffPoint(leftBouncePoint, morphSize: morphSize)
delay(seconds: 4.0, completion: foundOpponent)
}
動畫代碼:
func bounceOffPoint(bouncePoint: CGPoint, morphSize: CGSize) {
let originalCenter = center
UIView.animateWithDuration(animationDuration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.0, options: [], animations: {
self.center = bouncePoint
}, completion: {_ in
})
UIView.animateWithDuration(animateWithDuration, delay: animateWithDuration, usingSpringWithDamping: 0.7,initialSpringVelocity: 1.0, options: [], animations: {
self.center = originalCenter
},completion: {
delay(seconds: 0.1){
self.bounceOffPoint(bouncePoint, morphSize: morphSize)
}
})
}
截下來做2個頭像接觸之后的一個變形動畫
思路:根據(jù)左右2個頭像的frame分別設(shè)置動畫量愧,應(yīng)為2個頭像發(fā)生碰撞變形后的frame是不同的钾菊。對每個頭像進(jìn)行變形動畫
核心代碼:
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)//緩出的方式進(jìn)行動畫
circleLayer.addAnimation(morphAnimation, forKey:nil)
maskLayer.addAnimation(morphAnimation, forKey: nil)
接下來應(yīng)該是現(xiàn)實對手的數(shù)據(jù),并且改變頁面上方狀態(tài)label的顯示文字
思路:在開始動畫的過程中偎肃,對對手的頭像圖片和label的狀態(tài)進(jìn)行顯示结缚。
核心代碼如下:
//搜索中的方法
func foundOpponent() {
status.text = "Connecting..."
opponentAvatar.image = UIImage(named: "avatar-2")
opponentAvatar.name = "Ray"
delay(seconds: 4.0, completion: connectedToOpponent)
}
func connectedToOpponent() {
myAvatar.shouldTransitionToFinishedState = true//
opponentAvatar.shouldTransitionToFinishedState = true
delay(seconds: 1.0, completion: completed)
}
//最終的顯示狀態(tài)
func completed() {
status.text = "Ready to play"
UIView.animateWithDuration(0.2) {
self.vs.alpha = 1.0//顯示頭像之間的label
self.searchAgain.alpha = 1.0//顯示開始游戲的button按鈕
}
}
}
要實現(xiàn)的效果基本上是已經(jīng)出來了,還差一個就是怎樣才能讓搜索狀態(tài)信息完成之后使動畫結(jié)束呢
思路:用一個變量標(biāo)示是否結(jié)束動畫软棺,如是結(jié)束動畫红竭,執(zhí)行將動畫停止的方法,不在持續(xù)調(diào)用動畫的方法
核心代碼:
動畫完成后的判斷:
if self.shouldTransitionToFinishedState {
self.animateToSquare()
}
結(jié)束動畫:
func animateToSquare() {
isSquare = true
let squarePath = UIBezierPath(rect: 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
}
不在調(diào)用動畫方法的判斷:
if !self.isSquare{
self.bounceOffPoint(bouncePoint, morphSize: morphSize)
}
效果圖:
下載地址