Skip to content

Instantly share code, notes, and snippets.

@sathishvgs
Last active December 20, 2019 04:48
Show Gist options
  • Save sathishvgs/6a9adbba38933a9f6fa5ab5cea29d389 to your computer and use it in GitHub Desktop.
Save sathishvgs/6a9adbba38933a9f6fa5ab5cea29d389 to your computer and use it in GitHub Desktop.
Interactive Animation
//
// MusicListViewController.swift
// MusicPlayer
//
// Created by Sathish on 17/11/19.
// Copyright © 2019 Full. All rights reserved.
//
import UIKit
import UIKit.UIGestureRecognizerSubclass
class MusicListViewController: UIViewController {
@IBOutlet weak var aniView: UIView! // PanView
@IBOutlet weak var bottomCons: NSLayoutConstraint!
// @IBOutlet weak var heightCons: NSLayoutConstraint!
@IBOutlet weak var panInView: UIView! // Pan Inside View
// @IBOutlet weak var panInTopCons: NSLayoutConstraint!
var animator: UIViewPropertyAnimator?
var transitionAnimator: [UIViewPropertyAnimator] = []
var animationProcess: [CGFloat] = []
var currentState: State = .closed
override func viewDidLoad() {
super.viewDidLoad()
configPanGesture()
}
@IBAction func didTappedButton(_ sender: Any) {
let animator = UIViewPropertyAnimator(duration: 0.28, curve: .easeInOut) {
self.aniView.transform = CGAffineTransform(translationX: 260, y: 0)
}
animator.startAnimation()
}
func configPanGesture() {
let gesture = InstantPanGestureRecognizer(target: self, action: #selector(handlePan(recognizer:)))
aniView.addGestureRecognizer(gesture)
}
@objc
func handlePan(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
animateIfNeeded(state: currentState.invert)
print("********* BEGAN STATE STARTED *********")
transitionAnimator.forEach { $0.pauseAnimation() }
animationProcess = transitionAnimator.map { $0.fractionComplete }
case .changed:
var yPoint = -(recognizer.translation(in: aniView).y / 440)
if currentState == .open {
yPoint *= -1
}
if transitionAnimator[0].isReversed { yPoint *= -1 }
print("********* CHANGED STATE STARTED *********")
for (index, animator) in transitionAnimator.enumerated() {
print("Animation Process => \(animationProcess[index]) //// Translation => \(yPoint)")
animator.fractionComplete = yPoint + animationProcess[index]
}
case .ended:
let yVelocity = recognizer.velocity(in: aniView).y
let shouldClose = yVelocity > 0
print("********* ENDED STATE STARTED *********")
if yVelocity == 0 {
transitionAnimator.forEach { $0.continueAnimation(withTimingParameters: nil, durationFactor: 0) }
break
}
switch currentState {
case .open:
if !shouldClose && !transitionAnimator[0].isReversed { transitionAnimator.forEach { $0.isReversed = !$0.isReversed } }
if shouldClose && transitionAnimator[0].isReversed { transitionAnimator.forEach { $0.isReversed = !$0.isReversed } }
case .closed:
if shouldClose && !transitionAnimator[0].isReversed { transitionAnimator.forEach { $0.isReversed = !$0.isReversed } }
if !shouldClose && transitionAnimator[0].isReversed { transitionAnimator.forEach { $0.isReversed = !$0.isReversed } }
}
transitionAnimator.forEach { $0.continueAnimation(withTimingParameters: nil, durationFactor: 0) }
default:
break
}
}
func animateIfNeeded(state: State) {
guard transitionAnimator.isEmpty else { return }
let panInsideAnimator = UIViewPropertyAnimator(duration: 1, dampingRatio: 2) {
switch state {
case .open:
self.panInView.transform = CGAffineTransform(translationX: 0, y: -30)
case .closed:
self.panInView.transform = CGAffineTransform(translationX: 0, y: 30)
}
}
let transitionAnimator = UIViewPropertyAnimator(duration: 1, dampingRatio: 1)
transitionAnimator.addAnimations {
switch state {
case .open:
self.bottomCons.constant = 0
case .closed:
self.bottomCons.constant = -300
}
self.view.layoutIfNeeded()
}
transitionAnimator.addCompletion { position in
switch position {
case .start:
self.currentState = state.invert
case .end:
self.currentState = state
case .current: break
@unknown default: ()
}
switch self.currentState {
case .open:
self.bottomCons.constant = 0
case .closed:
self.bottomCons.constant = -300
}
print("Animation Removing...")
self.transitionAnimator.removeAll()
}
transitionAnimator.startAnimation()
panInsideAnimator.startAnimation()
self.transitionAnimator.append(transitionAnimator)
self.transitionAnimator.append(panInsideAnimator)
}
}
class InstantPanGestureRecognizer: UIPanGestureRecognizer {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if (self.state == UIGestureRecognizer.State.began) { return }
super.touchesBegan(touches, with: event)
self.state = UIGestureRecognizer.State.began
}
}
public enum State {
case open
case closed
var invert: State {
switch self {
case .open: return .closed
case .closed: return .open
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment