Created
October 13, 2018 13:53
-
-
Save thekan23/d58da01138a80554a4e8399a75db8953 to your computer and use it in GitHub Desktop.
DisplayLink + BezierPath를 사용한 애니메이션
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import UIKit | |
class ElasticView: UIView { | |
private let topControlPointView = UIView() | |
private let leftControlPointView = UIView() | |
private let rightControlPointView = UIView() | |
private let bottomControlPointView = UIView() | |
private let elasticShape = CAShapeLayer() | |
private lazy var displayLink: CADisplayLink = { | |
let displayLink = CADisplayLink(target: self, selector: #selector(updateLoop)) | |
displayLink.add(to: .current, forMode: .commonModes) | |
return displayLink | |
}() | |
func animateControlPoints() { | |
let overshootAmount: CGFloat = 10 // control point가 움직일 거리 | |
UIView.animate(withDuration: 5, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1.5, options: .curveLinear, animations: { | |
self.topControlPointView.center.y -= overshootAmount | |
self.leftControlPointView.center.x -= overshootAmount | |
self.rightControlPointView.center.x += overshootAmount | |
self.bottomControlPointView.center.y += overshootAmount | |
}) { _ in | |
UIView.animate(withDuration: 0.45, delay: 0.0, usingSpringWithDamping: 0.15, initialSpringVelocity: 5.5, options: .curveLinear, animations: { | |
self.positionControlPoints() | |
}) { _ in | |
self.stopUpdatesLoop() | |
} | |
} | |
} | |
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { | |
startUpdatesLoop() | |
animateControlPoints() | |
} | |
private func startUpdatesLoop() { | |
displayLink.isPaused = false | |
} | |
private func stopUpdatesLoop() { | |
displayLink.isPaused = true | |
} | |
private var controlPointViews: [UIView] { | |
return [topControlPointView, leftControlPointView, rightControlPointView, bottomControlPointView] | |
} | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
setupComponents() | |
} | |
required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
setupComponents() | |
} | |
@objc private func updateLoop() { | |
elasticShape.path = bezierPathForControlPoints() | |
} | |
private func setupComponents() { | |
elasticShape.fillColor = backgroundColor?.cgColor | |
elasticShape.path = UIBezierPath(rect: bounds).cgPath | |
layer.addSublayer(elasticShape) | |
for pointView in controlPointViews { | |
addSubview(pointView) | |
pointView.frame = CGRect(x: 0, y: 0, width: 5, height: 5) | |
pointView.backgroundColor = .blue | |
} | |
positionControlPoints() | |
} | |
private func positionControlPoints() { | |
topControlPointView.center = CGPoint(x: bounds.midX, y: 0) | |
leftControlPointView.center = CGPoint(x: 0, y: bounds.midY) | |
rightControlPointView.center = CGPoint(x: bounds.maxX, y: bounds.midY) | |
bottomControlPointView.center = CGPoint(x: bounds.midX, y: bounds.maxY) | |
} | |
// control points를 애니메이팅 할 때 이 메서드를 다시 호출해야 한다 | |
private func bezierPathForControlPoints() -> CGPath { | |
let path = UIBezierPath() | |
// presentation 프로퍼티를 사용한 이유는 animation 도중 view의 실시간 위치를 얻기 위함 | |
let top = topControlPointView.layer.presentation()?.position | |
let left = leftControlPointView.layer.presentation()?.position | |
let right = rightControlPointView.layer.presentation()?.position | |
let bottom = bottomControlPointView.layer.presentation()?.position | |
let width = frame.width | |
let height = frame.height | |
print("top: \(top!), left: \(left!), right: \(right!), bottom: \(bottom!), width: \(width), height: \(height)") | |
// 직사각형의 모서리와 모서리에 곡선을 더하면서 path를 생성 | |
path.move(to: .zero) | |
path.addQuadCurve(to: CGPoint(x: width, y: 0), controlPoint: top!) // 우상단 | |
path.addQuadCurve(to: CGPoint(x: width, y: height), controlPoint: right!) // 우하단 | |
path.addQuadCurve(to: CGPoint(x: 0, y: height), controlPoint: bottom!) | |
path.addQuadCurve(to: .zero, controlPoint: left!) | |
return path.cgPath | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment