Skip to content

Instantly share code, notes, and snippets.

@hachinobu
Created April 19, 2020 12:39
Show Gist options
  • Save hachinobu/42ae89e616b0b7b2790d18ff9c132c5d to your computer and use it in GitHub Desktop.
Save hachinobu/42ae89e616b0b7b2790d18ff9c132c5d to your computer and use it in GitHub Desktop.
class ViewController: UIViewController {
enum ImageViewState: Int {
case open
case close
var opposite: ImageViewState {
switch self {
case .open:
return .close
case .close:
return .open
}
}
}
private lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "background")
imageView.isUserInteractionEnabled = true
return imageView
}()
private let blurView: UIVisualEffectView = {
let blurView = UIVisualEffectView()
blurView.isUserInteractionEnabled = false
return blurView
}()
private var imageViewTopConstraint = NSLayoutConstraint()
private var offset: CGFloat {
return view.frame.maxY - 80
}
// UIViewPropertyAnimatorの生成
private var animator = UIViewPropertyAnimator(duration: 3.0, curve: .easeInOut)
private var animationProgress: CGFloat = 0.0
private var imageViewState: ImageViewState = .close
override func viewDidLoad() {
super.viewDidLoad()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageViewTopConstraint = imageView.topAnchor.constraint(equalTo: view.topAnchor)
view.addSubview(imageView)
NSLayoutConstraint.activate(
[
imageViewTopConstraint,
imageView.leftAnchor.constraint(equalTo: view.leftAnchor),
imageView.rightAnchor.constraint(equalTo: view.rightAnchor),
imageView.heightAnchor.constraint(equalTo: view.heightAnchor)
]
)
blurView.translatesAutoresizingMaskIntoConstraints = false
imageView.addSubview(blurView)
NSLayoutConstraint.activate(
[
blurView.topAnchor.constraint(equalTo: imageView.topAnchor),
blurView.leftAnchor.constraint(equalTo: imageView.leftAnchor),
blurView.rightAnchor.constraint(equalTo: imageView.rightAnchor),
blurView.bottomAnchor.constraint(equalTo: imageView.bottomAnchor)
]
)
let recognizer = UIPanGestureRecognizer()
recognizer.addTarget(self, action: #selector(self.imageViewPanned(recognizer:)))
imageView.addGestureRecognizer(recognizer)
}
private func setupAnimator(state: ImageViewState) {
// 逆再生をoff
animator.isReversed = false
// アニメーション実行中の場合はアニメーションを設定しない
if animator.isRunning {
return
}
// アニメーションの追加
animator.addAnimations {
switch state {
case .close:
self.imageViewTopConstraint.constant = -self.offset
case .open:
self.imageViewTopConstraint.constant = 0
}
self.view.layoutIfNeeded()
}
// アニメーションの追加
animator.addAnimations {
switch state {
case .close:
self.blurView.effect = UIBlurEffect(style: .light)
case .open:
self.blurView.effect = nil
}
}
  // アニメーション完了時の処理
animator.addCompletion { position in
switch position {
case .start:
self.imageViewState = state
case .end:
self.imageViewState = state.opposite
case .current: ()
}
switch self.imageViewState {
case .close:
self.imageViewTopConstraint.constant = 0.0
case .open:
self.imageViewTopConstraint.constant = -self.offset
}
}
}
@objc private func imageViewPanned(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
// Animatorの設定
setupAnimator(state: imageViewState)
// アニメーションの一時停止
animator.pauseAnimation()
// アニメーションの進捗率を保持
animationProgress = animator.fractionComplete
case .changed:
let translation = recognizer.translation(in: imageView)
var fraction = -translation.y / offset
if imageViewState == .open { fraction *= -1 }
// ユーザー操作とアニメーションの進捗率を連動させインタラクティブアニメーションを実現するキーポイント
animator.fractionComplete = fraction + animationProgress
case .ended:
let yVelocity = recognizer.velocity(in: imageView).y
if yVelocity == 0 {
// アニメーション再開
animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
}
switch imageViewState {
case .close:
if yVelocity > 0 {
// 逆再生モードON
animator.isReversed = true
}
case .open:
if yVelocity < 0 {
// 逆再生モードON
animator.isReversed = true
}
}
// アニメーション再開
animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
default: ()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment