新建一个Swift工程。然后首先要做的就是画出动画的曲线。
画曲线
新建一个CurvedView 类
class CurvedView: UIView {
override func draw(_ rect: CGRect) {
}
}
然后在viewDidLoad中创建一个CurvedView对象并且添加到view上。
let curvedView = CurvedView(frame: view.frame)
curvedView.backgroundColor = .yellow
view.addSubview(curvedView)
接下来要画曲线,先画两个点后运行,发现是一条很细的直线。
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 200))
let endPoint = CGPoint(x: 200, y: 200)
path.addLine(to:endPoint)
path.stroke()
}
接下来要用到addCurve,在苹果官方文档中说到,此方法将当前点的三次贝塞尔曲线附加到终点参数指定的终点。这两个控制点定义了线段的曲率。看起来晕头转向的,写完代码后运行看的比较直观。
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 200))
let endPoint = CGPoint(x: 450, y: 200)
path.lineWidth = 3
let cp1 = CGPoint(x: 100, y: 100)
let cp2 = CGPoint(x: 200, y: 300)
path.addCurve(to: endPoint, controlPoint1: cp1, controlPoint2: cp2)
path.stroke()
}
2. 添加动画
把画的UIBezierPath分离成一个方法,这样就方便我们使用
func customPath() -> UIBezierPath {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 200))
let endPoint = CGPoint(x: 450, y: 200)
path.lineWidth = 3
let cp1 = CGPoint(x: 100, y: 100)
let cp2 = CGPoint(x: 200, y: 300)
path.addCurve(to: endPoint, controlPoint1: cp1, controlPoint2: cp2)
path.stroke()
return path
}
class CurvedView: UIView {
override func draw(_ rect: CGRect) {
let path = customPath()
path.lineWidth = 3
path.stroke()
}
}
添加一张图片,并且将刚才的path赋值给animation,设置好动画时长等,然后将动画添加给图片。
let imageView = UIImageView(image: UIImage(named: "heart"))
imageView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = customPath().cgPath
animation.duration = 2
animation.isRemovedOnCompletion = true
animation.fillMode = .forwards
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
imageView.layer.add(animation, forKey: nil)
view.addSubview(imageView)
接下来使用drand48给图片一个随机的大小。20 - 30
let dimension = 20 + drand48() * 10
imageView.frame = CGRect(x: 0, y: 0, width: dimension, height: dimension)
为view 添加一个手势
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
每次点击都生成十个imageView。
@objc func handleTap() {
(0...10).forEach { (_) in
generateAnimatedViews()
}
}
func generateAnimatedViews() {
let imageView = UIImageView(image: UIImage(named: "heart"))
let dimension = 20 + drand48() * 10
imageView.frame = CGRect(x: 0, y: 0, width: dimension, height: dimension)
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = customPath().cgPath
animation.duration = 2
animation.isRemovedOnCompletion = true
animation.fillMode = .forwards
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
imageView.layer.add(animation, forKey: nil)
view.addSubview(imageView)
}
到这里有个问题就是每个图片的path都是一样的,所以会叠加到一起,所以需要调整一下path。 在customPath方法中给cp1 和cp2添加随机性。
let randomYShift = 200 + drand48() * 300
let cp1 = CGPoint(x: 100, y: 100 - randomYShift)
let cp2 = CGPoint(x: 200, y: 300 + randomYShift)
这里又发现了一个问题就是所有的图片都在一个X上面,这是因为它们的动画时长都一样,所以需要给动画时长页添加一点随机性。 在generateAnimatedViews方法中修改:
animation.duration = 2 + drand48() * 3
为了图片随机,在generateAnimatedViews方法中修改:
let image = drand48() > 0.5 ? UIImage(named: "heart") : UIImage(named: "thumbs_up")
let imageView = UIImageView(image: image)
结束。完整代码如下:
|