So I have a UIView
with a PanGestureRecognizer
attached that allows me to move the view up and down. As I move the view up, I want to decrease my button's alpha and when I move the view down I want to increase it back to normal.
I have tried to animate the button separately when it snaps into a position but this then breaks my PropertyAnimator
.
My current implementation works find, provided that my finger is on the view as this returns the .changed
state from the recogniser. But I also have implementation that snaps the view into position when the view is release at a certain point along the y axis. The problem is that it doesn't hit the .changed state and therefore change my hideAnimator.fractionComplete
leaving the button half way through its animation.
Initialise property animator method
func initPropertyAnimator() {
hideAnimator = UIViewPropertyAnimator(duration: 0.18, curve: .easeInOut, animations: {
self.requestButton.alpha = 0
})
}
Recogniser states in panGesture(recognizer: UIPanGestureRecognizer)
let currentY = self.frame.minY
switch recognizer.state {
case .began:
print("Began")
case .changed:
// Property animator
let offset: CGFloat = window!.frame.height-194
let percentage: CGFloat = (offset-currentY)/100
print(percentage)
hideAnimator?.fractionComplete = CGFloat(percentage)
case .ended:
// Snap back below 80 (y)
if currentY < 80 || currentY > 80 && currentY < halfWayPoint
{
self.animator?.stopAnimation(true)
UIView.animate(withDuration:0.62, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations:
{
recognizer.view!.frame = CGRect(x: 0, y: 80, width: self.frame.width, height: self.frame.height)
self.requestButton.alpha = 0
self.houseView.frame = CGRect(x: 16, y: 87, width: self.boxWidth, height: 68)
self.officeView.frame = CGRect(x: 16, y: 171, width: self.boxWidth, height: 68)
self.carView.frame = CGRect(x: 16, y: 255, width: self.boxWidth, height: 68)
self.gardenView.frame = CGRect(x: 16, y: 339, width: self.boxWidth, height: 68)
}, completion: { (true) in
})
}
// Snap back above partial (y)
if currentY > partialY || currentY < partialY && currentY > halfWayPoint
{
self.animator?.stopAnimation(true)
UIView.animate(withDuration:0.62, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations:
{
recognizer.view!.frame = CGRect(x: 0, y: partialY, width: self.frame.width, height: self.frame.height)
self.requestButton.alpha = 1
self.houseView.frame = CGRect(x: 16, y: 87+200, width: self.boxWidth, height: 68)
self.officeView.frame = CGRect(x: 16, y: 171+200, width: self.boxWidth, height: 68)
self.carView.frame = CGRect(x: 16, y: 255+200, width: self.boxWidth, height: 68)
self.gardenView.frame = CGRect(x: 16, y: 339+200, width: self.boxWidth, height: 68)
}, completion: { (true) in
})
}
I solved the issue by adding a boolean to check whether or not the view is currently expanded and then depending on true or false I load the corresponding animations. I know this is not the best way of doing things as you are able to return the animation to its original place without having to write so much code. Regardless, it works, so I'll roll with it.
By the way, I am very much open to anyone who can help refine this code, it definitely is in need of some TLC.
Here is my entire method that handles my panGestureRecogniser
// MARK: - Pan gesture
@objc func panGesture(recognizer: UIPanGestureRecognizer) {
let window = UIApplication.shared.keyWindow
let translation = recognizer.translation(in: self)
let currentY = self.frame.minY
let partialY = (window?.frame.height)!-194
let halfWayPoint = ((window?.frame.height)!/2)-40
self.frame = CGRect(x: 0, y: currentY + translation.y, width: self.frame.width, height: self.frame.height)
recognizer.setTranslation(CGPoint.zero, in: self)
switch recognizer.state {
case .began:
print("Began")
if isExpanded
{
animator = UIViewPropertyAnimator(duration: 0.18, curve: .easeInOut, animations: {
self.requestButton.alpha = 1
self.houseView.alpha = 0
self.officeView.alpha = 0
self.gardenView.alpha = 0
self.carView.alpha = 0
self.houseView.frame = CGRect(x: 16, y: 87+200, width: self.boxWidth, height: 68)
self.officeView.frame = CGRect(x: 16, y: 171+200, width: self.boxWidth, height: 68)
self.carView.frame = CGRect(x: 16, y: 255+200, width: self.boxWidth, height: 68)
self.gardenView.frame = CGRect(x: 16, y: 339+200, width: self.boxWidth, height: 68)
})
self.animator?.isReversed = true
}
else
{
animator = UIViewPropertyAnimator(duration: 0.18, curve: .easeInOut, animations: {
self.requestButton.alpha = 0
self.houseView.alpha = 1
self.officeView.alpha = 1
self.gardenView.alpha = 1
self.carView.alpha = 1
self.houseView.frame = CGRect(x: 16, y: 87, width: self.boxWidth, height: 68)
self.officeView.frame = CGRect(x: 16, y: 171, width: self.boxWidth, height: 68)
self.carView.frame = CGRect(x: 16, y: 255, width: self.boxWidth, height: 68)
self.gardenView.frame = CGRect(x: 16, y: 339, width: self.boxWidth, height: 68)
})
self.animator?.isReversed = false
}
animator?.startAnimation()
animator?.pauseAnimation()
case .changed:
// Property animator
let offset: CGFloat = window!.frame.height-194
let fraction: CGFloat = (offset-currentY)/4
let percentage = fraction/100
print(percentage)
animator?.fractionComplete = CGFloat(percentage)
// Prevent scrolling up past y:0
if currentY <= 0
{
self.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
}
case .ended:
// Snap to 80 (y) (expanded)
if currentY < 80 || currentY > 80 && currentY < halfWayPoint
{
self.animator?.stopAnimation(true)
UIView.animate(withDuration:0.62, delay: 0.0, usingSpringWithDamping: 0.62, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations:
{
recognizer.view!.frame = CGRect(x: 0, y: 80, width: self.frame.width, height: self.frame.height)
self.requestButton.alpha = 0
self.houseView.alpha = 1
self.officeView.alpha = 1
self.gardenView.alpha = 1
self.carView.alpha = 1
self.houseView.frame = CGRect(x: 16, y: 87, width: self.boxWidth, height: 68)
self.officeView.frame = CGRect(x: 16, y: 171, width: self.boxWidth, height: 68)
self.carView.frame = CGRect(x: 16, y: 255, width: self.boxWidth, height: 68)
self.gardenView.frame = CGRect(x: 16, y: 339, width: self.boxWidth, height: 68)
}, completion: { (true) in
self.isExpanded = true
})
}
// Snap back to partial (y) (original position)
if currentY > partialY || currentY < partialY && currentY > halfWayPoint
{
self.animator?.stopAnimation(true)
UIView.animate(withDuration:0.62, delay: 0.0, usingSpringWithDamping: 0.62, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations:
{
recognizer.view!.frame = CGRect(x: 0, y: partialY, width: self.frame.width, height: self.frame.height)
self.requestButton.alpha = 1
self.houseView.alpha = 0
self.officeView.alpha = 0
self.gardenView.alpha = 0
self.carView.alpha = 0
self.houseView.frame = CGRect(x: 16, y: 87+200, width: self.boxWidth, height: 68)
self.officeView.frame = CGRect(x: 16, y: 171+200, width: self.boxWidth, height: 68)
self.carView.frame = CGRect(x: 16, y: 255+200, width: self.boxWidth, height: 68)
self.gardenView.frame = CGRect(x: 16, y: 339+200, width: self.boxWidth, height: 68)
}, completion: {(true) in
self.isExpanded = false
})
}
case .cancelled:
print("Cancelled")
case .failed:
print("Failed")
default:
print("Default")
}
}