Reputation: 31
I need some help with dragging UIView
to reveal, menu view underneath main view.
I have two UIView
s.
menuView
- includes menu buttons and labels
and mainView
- located over menuView
.
I want to drag the main view from left edge to show menu items and snap the main view to a specific position. I am able to get the dragging to the right working but I cannot set it back to original position when dragging left.
here is my codes I have been playing around with but no success.
I also wanted to make mainView
smaller as I drag it to right.
any help will be greatly appreciated.
Note: PanGesture is attached to mainView
.
@IBAction func dragMenu(_ sender: UIPanGestureRecognizer) {
let mview = sender.view!
let originalCenter = CGPoint(x: self.mainView.bounds.width/2, y: self.mainView.bounds.height/2)
switch sender.state {
case .changed:
if let mview = sender.view {
mview.center.x = mview.center.x + sender.translation(in: view).x
sender.setTranslation(CGPoint.zero, in: view)
}
case .ended:
let DraggedHalfWayRight = mview.center.x > view.center.x
if DraggedHalfWayRight {
//dragginToRight
showMenu = !showMenu
self.mainViewRight.constant = 200
self.mainTop.constant = 50
self.mainBottom.constant = 50
self.mainLeft.constant = 200
} else //dragging left and set it back to original position.
{
mview.center = originalCenter
showMenu = !showMenu
}
default:
break
}
}
Upvotes: 0
Views: 1574
Reputation: 437622
I'd suggest a few things:
When you're done dragging the menu off, make sure to set its alpha
to zero (so that, if you were in portrait and go to landscape, you don't suddenly see the menu sitting there).
I personally just adjust the transform
of the dragged view and avoid resetting the translation of the gesture back to zero all the time. That's a matter of personal preference.
I'd make the view controller the delegate for the gesture and implement gestureRecognizerShouldBegin
(because if the menu is hidden, you don't want to recognize swipes to try to hide it again; and if it's not hidden, you don't want to recognize swipes to show it).
When figuring out whether to complete the gesture or not, I also consider the velocity of the gesture (e.g. so a little flick will dismiss/show the animated view).
Thus:
class ViewController: UIViewController {
@IBOutlet weak var menuView: UIView!
var isMenuVisible = true
@IBAction func dragMenu(_ gesture: UIPanGestureRecognizer) {
let translationX = gesture.translation(in: gesture.view!).x
switch gesture.state {
case .began:
// if the menu is not visible, make sure it's off screen and then make it visible
if !isMenuVisible {
menuView.transform = CGAffineTransform(translationX: gesture.view!.bounds.width, y: 0)
menuView.alpha = 1
}
fallthrough
case .changed:
if isMenuVisible {
menuView.transform = CGAffineTransform(translationX: translationX, y: 0)
} else {
menuView.transform = CGAffineTransform(translationX: gesture.view!.bounds.width + translationX, y: 0)
}
case .ended:
let shouldComplete: Bool
if isMenuVisible {
shouldComplete = translationX > gesture.view!.bounds.width / 2 || gesture.velocity(in: gesture.view!).x > 0
} else {
shouldComplete = -translationX > gesture.view!.bounds.width / 2 || gesture.velocity(in: gesture.view!).x < 0
}
UIView.animate(withDuration: 0.25, animations: {
if self.isMenuVisible && shouldComplete || !self.isMenuVisible && !shouldComplete {
self.menuView.transform = CGAffineTransform(translationX: gesture.view!.bounds.width, y: 0)
} else {
self.menuView.transform = .identity
}
if shouldComplete{
self.isMenuVisible = !self.isMenuVisible
}
}, completion: { _ in
self.menuView.alpha = self.isMenuVisible ? 1 : 0
})
default:
break
}
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gesture: UIGestureRecognizer) -> Bool {
guard let gesture = gesture as? UIPanGestureRecognizer else { return true }
let translationX = gesture.translation(in: gesture.view!).x
return isMenuVisible && translationX > 0 || !isMenuVisible && translationX < 0
}
}
Personally, I prefer to use a separate screen edge gesture recognizer to pull them menu back on screen (you don't really want it to recognize gestures anywhere, but just on that right edge). Another virtue of this approach is that by keeping "show" and "hide" in different functions, the code is a lot more readable (IMHO):
class ViewController: UIViewController {
@IBOutlet weak var menuView: UIView!
var isMenuVisible = true
@IBAction func handleScreenEdgeGesture(_ gesture: UIScreenEdgePanGestureRecognizer) {
let translationX = gesture.translation(in: gesture.view!).x
switch gesture.state {
case .began:
menuView.transform = CGAffineTransform(translationX: gesture.view!.bounds.width, y: 0)
menuView.alpha = 1
fallthrough
case .changed:
menuView.transform = CGAffineTransform(translationX: gesture.view!.bounds.width + translationX, y: 0)
case .ended:
let shouldComplete = -translationX > gesture.view!.bounds.width / 2 || gesture.velocity(in: gesture.view!).x < 0
UIView.animate(withDuration: 0.25, delay:0, options: .curveEaseOut, animations: {
if shouldComplete {
self.menuView.transform = .identity
self.isMenuVisible = !self.isMenuVisible
} else {
self.menuView.transform = CGAffineTransform(translationX: gesture.view!.bounds.width, y: 0)
}
}, completion: { _ in
self.menuView.alpha = self.isMenuVisible ? 1 : 0
})
default:
break
}
}
@IBAction func dragMenu(_ gesture: UIPanGestureRecognizer) {
let translationX = gesture.translation(in: gesture.view!).x
switch gesture.state {
case .began, .changed:
menuView.transform = CGAffineTransform(translationX: translationX, y: 0)
case .ended:
let shouldComplete = translationX > gesture.view!.bounds.width / 2 || gesture.velocity(in: gesture.view!).x > 0
UIView.animate(withDuration: 0.25, delay:0, options: .curveEaseOut, animations: {
if shouldComplete {
self.menuView.transform = CGAffineTransform(translationX: gesture.view!.bounds.width, y: 0)
self.isMenuVisible = !self.isMenuVisible
} else {
self.menuView.transform = .identity
}
}, completion: { _ in
self.menuView.alpha = self.isMenuVisible ? 1 : 0
})
default:
break
}
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIScreenEdgePanGestureRecognizer {
return !isMenuVisible
} else if let gesture = gestureRecognizer as? UIPanGestureRecognizer {
let translationX = gesture.translation(in: gesture.view!).x
return isMenuVisible && translationX > 0
}
return true
}
}
Upvotes: 1