I am writing an iPad app that presents user documents similar to the way Pages presents them (as large icons of the actual document). I also want to mimic the jiggling beha
Here is the Swift 4.2 version of @mientus' code (which is itself an update of Paul Popiel's version), as an extension of CALayer:
extension CALayer {
private enum WigglingAnimationKey: String {
case position = "wiggling_position_animation"
case transform = "wiggling_transform_animation"
}
func startWiggling() {
let duration = 0.25
let displacement = 1.0
let negativeDisplacement = displacement * -1
let rotationAngle = Measurement(value: 2, unit: UnitAngle.degrees)
// Position animation
let positionAnimation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.position))
positionAnimation.beginTime = 0.8
positionAnimation.duration = duration
positionAnimation.values = [
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint.zero),
NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement))
]
positionAnimation.calculationMode = .linear
positionAnimation.isRemovedOnCompletion = false
positionAnimation.repeatCount = .greatestFiniteMagnitude
positionAnimation.beginTime = CFTimeInterval(Float(Int.random(in: 0...25)) / 100)
positionAnimation.isAdditive = true
// Rotation animation
let transformAnimation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.transform))
transformAnimation.beginTime = 2.6
transformAnimation.duration = duration
transformAnimation.valueFunction = CAValueFunction(name: .rotateZ)
transformAnimation.values = [
CGFloat(rotationAngle.converted(to: .radians).value * -1),
CGFloat(rotationAngle.converted(to: .radians).value),
CGFloat(rotationAngle.converted(to: .radians).value * -1)
]
transformAnimation.calculationMode = .linear
transformAnimation.isRemovedOnCompletion = false
transformAnimation.repeatCount = .greatestFiniteMagnitude
transformAnimation.isAdditive = true
transformAnimation.beginTime = CFTimeInterval(Float(Int.random(in: 0...25)) / 100)
self.add(positionAnimation, forKey: WigglingAnimationKey.position.rawValue)
self.add(transformAnimation, forKey: WigglingAnimationKey.transform.rawValue)
}
func stopWiggling() {
self.removeAnimation(forKey: WigglingAnimationKey.position.rawValue)
self.removeAnimation(forKey: WigglingAnimationKey.transform.rawValue)
}
}
Usage (where anyLayer is a CALayer):
// Start animating.
anyLayer.startWiggling()
// Stop animating.
anyLayer.stopWiggling()