Skip to content

Instantly share code, notes, and snippets.

@zaimramlan
Created November 13, 2018 19:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zaimramlan/2dee8da8c95ecd520dd9985514d09dce to your computer and use it in GitHub Desktop.
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.
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