I'm designing an animation so that the 5 objects can move at a 45 degree angle along an arc each time the red circle is clicked.
However, use animation.fillMode = CAMediaTimingFillMode.forwards , but when I try the first animation, the view is not saved and returns to the beginning.
And after that, the touch animation is applied to the second element, the orange circle, and not the red one.
As expected, there seemed to be a difference between the view seen after the view animation and the actual view, but I couldn't figure out why.
Does anyone know about the cause of this?
I am attaching the code and a gif image that actually works with this code. I'm a former Android developer, so I'm relatively weak on iOS.
import UIKit
class ArcMenuTest: UIView {
private lazy var circle1View: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
view.clipsToBounds = true
return view
}()
private lazy var circle2View: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .orange
view.clipsToBounds = true
return view
}()
private lazy var circle3View: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .yellow
view.clipsToBounds = true
return view
}()
private lazy var circle4View: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .green
view.clipsToBounds = true
return view
}()
private lazy var circle5View: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .blue
view.clipsToBounds = true
return view
}()
var circle1Radian = 180
var circle2Radian = 225
var circle3Radian = 270
var circle4Radian = 315
var circle5Radian = 360
required init?(coder: NSCoder) {
super.init(coder: coder)
self.setup()
setListener()
}
private func setup() {
self.addSubview(self.circle1View)
NSLayoutConstraint.activate([
self.circle1View.widthAnchor.constraint(equalToConstant: 30),
self.circle1View.heightAnchor.constraint(equalToConstant: 30)
])
self.addSubview(self.circle2View)
NSLayoutConstraint.activate([
self.circle2View.widthAnchor.constraint(equalToConstant: 30),
self.circle2View.heightAnchor.constraint(equalToConstant: 30)
])
self.addSubview(self.circle3View)
NSLayoutConstraint.activate([
self.circle3View.widthAnchor.constraint(equalToConstant: 30),
self.circle3View.heightAnchor.constraint(equalToConstant: 30)
])
self.addSubview(self.circle4View)
NSLayoutConstraint.activate([
self.circle4View.widthAnchor.constraint(equalToConstant: 30),
self.circle4View.heightAnchor.constraint(equalToConstant: 30)
])
self.addSubview(self.circle5View)
NSLayoutConstraint.activate([
self.circle5View.widthAnchor.constraint(equalToConstant: 30),
self.circle5View.heightAnchor.constraint(equalToConstant: 30)
])
}
override func layoutSubviews() {
super.layoutSubviews()
self.circle1View.layer.cornerRadius = self.circle1View.frame.width / 2.0
self.circle1View.center = self.getPoint(for: 180)
self.circle2View.layer.cornerRadius = self.circle1View.frame.width / 2.0
self.circle2View.center = self.getPoint(for: 225)
self.circle3View.layer.cornerRadius = self.circle1View.frame.width / 2.0
self.circle3View.center = self.getPoint(for: 270)
self.circle4View.layer.cornerRadius = self.circle1View.frame.width / 2.0
self.circle4View.center = self.getPoint(for: 315)
self.circle5View.layer.cornerRadius = self.circle1View.frame.width / 2.0
self.circle5View.center = self.getPoint(for: 360)
}
private func getPoint(for angle: Int) -> CGPoint {
let arcCenter = CGPoint(x: bounds.size.width / 2, y: bounds.size.height)
let circleRadius = bounds.size.width / 2
let circlePath = UIBezierPath(arcCenter: arcCenter, radius: circleRadius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 2, clockwise: true)
// 1
let radius = bounds.size.width / 2
// 2
let radian = Double(angle) * Double.pi / Double(180)
// 3
let newCenterX = radius + radius * cos(radian)
let newCenterY = radius + radius * sin(radian)
return CGPoint(x: newCenterX, y: newCenterY)
}
private func animate(view: UIView, path: UIBezierPath) {
// 1
let animation = CAKeyframeAnimation(keyPath: "position")
// 2
animation.path = path.cgPath
// 3
animation.repeatCount = 1
// 4
animation.autoreverses = false
animation.duration = 0.5
animation.fillMode = CAMediaTimingFillMode.forwards;
animation.isRemovedOnCompletion = false
// 5
CATransaction.begin()
view.layer.add(animation, forKey: "animation")
CATransaction.setDisableActions(true)
CATransaction.setCompletionBlock({
if(view == self.circle1View) {
self.circle1Radian += 45
self.circle1View.center = self.getPoint(for: self.circle1Radian)
} else if(view == self.circle2View) {
self.circle2Radian += 45
self.circle2View.center = self.getPoint(for: self.circle2Radian)
} else if(view == self.circle3View) {
self.circle3Radian += 45
self.circle3View.center = self.getPoint(for: self.circle3Radian)
} else if(view == self.circle4View) {
self.circle4Radian += 45
self.circle4View.center = self.getPoint(for: self.circle4Radian)
} else if(view == self.circle5View) {
self.circle5Radian += 45
self.circle5View.center = self.getPoint(for: self.circle5Radian)
}
})
CATransaction.commit()
}
func updateView() {
circle1Radian += 45
circle2Radian += 45
circle3Radian += 45
circle4Radian += 45
circle5Radian += 45
// self.circle1View.center = self.getPoint(for: circle1Radian)
// self.circle2View.center = self.getPoint(for: circle2Radian)
// self.circle3View.center = self.getPoint(for: circle3Radian)
// self.circle4View.center = self.getPoint(for: circle4Radian)
// self.circle5View.center = self.getPoint(for: circle5Radian)
}
func startAnimating(uiView : UIView, radius : Int) {
// 1
let path = UIBezierPath()
// 2
let initialPoint = self.getPoint(for: radius)
let endPoint = radius + 45
uiView.center = self.getPoint(for: endPoint)
path.move(to: initialPoint)
// 3
for angle in radius...endPoint {
path.addLine(to: self.getPoint(for: angle))
}
//for angle in 1...270 { path.addLine(to: self.getPoint(for: angle)) }
// 4
path.close()
// 5
self.animate(view: uiView, path: path)
}
private func setListener() {
circle1View.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onCircle1Click)))
}
@objc private func onCircle1Click() {
print("??????")
startAnimating(uiView: circle1View, radius: circle1Radian)
startAnimating(uiView: circle2View, radius: circle2Radian)
startAnimating(uiView: circle3View, radius: circle3Radian)
startAnimating(uiView: circle4View, radius: circle4Radian)
startAnimating(uiView: circle5View, radius: circle5Radian)
//updateView()
}
}
