Created
November 13, 2018 19:15
-
-
Save zaimramlan/2dee8da8c95ecd520dd9985514d09dce to your computer and use it in GitHub Desktop.
A pannable view class to quickly apply pan gestures onto a UIView to slide vertically, reset to original position and close the view.
This file contains 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
class CustomPannableView: UIView { | |
// MARK: Properties | |
private typealias BottomSheetStyle = StyleConstants.Views.BottomSheet | |
private var minimumVelocityToHide = BottomSheetStyle.minimumPanningVelocityToAutoHide | |
private var minimumScreenRatioToHide = BottomSheetStyle.minimumPositionRatioToAutoHide | |
private var animationDuration = BottomSheetStyle.animationDuration | |
private var parentView: UIView? | |
private var completion: (() -> ())? | |
// MARK: Gesture | |
func setupPanGesture(within parent: UIView, completion: (() -> ())?) { | |
self.parentView = parent | |
self.completion = completion | |
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:))) | |
addGestureRecognizer(panGesture) | |
} | |
private func slideViewVertically(to y: CGFloat, animated: Bool = false, withCompletion: Bool = false) { | |
if animated { | |
UIView.animate(withDuration: animationDuration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseInOut, animations: { | |
self.parentView?.frame.origin = CGPoint(x: 0, y: y) | |
}, completion: { (isCompleted) in | |
if withCompletion && isCompleted { self.completion?() } | |
}) | |
return | |
} | |
else { | |
self.parentView?.frame.origin = CGPoint(x: 0, y: y) | |
if withCompletion { self.completion?() } | |
} | |
} | |
@objc private func onPan(_ panGesture: UIPanGestureRecognizer) { | |
guard let parent = self.parentView else { return } | |
switch panGesture.state { | |
// slide view to follow touch on panning begin/continuation | |
case .began, .changed: | |
let translation = panGesture.translation(in: parent) | |
if translation.y >= 0 { | |
slideViewVertically(to: translation.y) | |
} | |
// close view if should close, | |
// reset view if otherwise | |
case .ended: | |
let translation = panGesture.translation(in: parent) | |
let velocity = panGesture.velocity(in: parent) | |
// determine based on final touch position or velocity | |
let shouldClose = | |
(translation.y > parent.frame.size.height * minimumScreenRatioToHide) || | |
(velocity.y > minimumVelocityToHide) | |
if shouldClose { | |
self.slideViewVertically(to: parent.frame.size.height, animated: true, withCompletion: true) | |
} else { | |
self.slideViewVertically(to: 0, animated: true, withCompletion: false) | |
} | |
// reset view for undefined pan gesture state | |
default: | |
self.slideViewVertically(to: 0, animated: true, withCompletion: false) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment