I am trying to make a hamburger menu for my app programmatically and using no plugins. I am starting with the basics, so just trying to get 2 UIViews working together when the user swipes right. The 2 views are the main view and the hamburger menu.
So far the UIViews load in the correct place and when the user swipes right the sidebar slides along. However, when the user lets go the UIView also slides up to the center of the UIView is pinned to the top of the screen. See images below:
Once it gets lost up there I cannot pull it back down. I can still swipe left and right but the center stays constrained to the top of the screen.
I've looked through my code and cannot see what I am doing wrong here?
Here is my code:
class gestureSwipe: UIViewController, UIGestureRecognizerDelegate {
let screenHeight = UIScreen.main.bounds.height
let screenWidth = UIScreen.main.bounds.width
var trayOriginalCenter: CGPoint!
var sideBarSwipeLeftOffset: CGFloat!
var siderBarSwipeRight: CGPoint!
var sideBarSwipeLeft: CGPoint!
let sideBarUIView: UIView! = {
let sideBarUIView = UIView()
sideBarUIView.backgroundColor = UIColor(red:1.0, green:0.0, blue:0.0, alpha:1.0)
sideBarUIView.translatesAutoresizingMaskIntoConstraints = false
sideBarUIView.isUserInteractionEnabled = true
return sideBarUIView
}()
let mainView: UIView = {
let mainView = UIView()
mainView.backgroundColor = UIColor(red:0.0, green:1.0, blue:0.0, alpha:1.0)
mainView.translatesAutoresizingMaskIntoConstraints = false
mainView.isUserInteractionEnabled = true
return mainView
}()
override func viewDidLoad() {
super.viewDidLoad()
let settingsButton = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(HandleSettings))
navigationItem.leftBarButtonItem = settingsButton
view.backgroundColor = UIColor.white
view.addSubview(mainView)
view.addSubview(sideBarUIView)
let SideBarPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPan(sender:)))
let MainViewPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPan(sender:)))
mainView.addGestureRecognizer(MainViewPanGestureRecognizer)
sideBarUIView.addGestureRecognizer(SideBarPanGestureRecognizer)
sideBarSwipeLeftOffset = 80
siderBarSwipeRight = sideBarUIView.center
sideBarSwipeLeft = CGPoint(x: sideBarUIView.center.x + sideBarSwipeLeftOffset, y: sideBarUIView.center.y)
setupLayout()
}
@IBAction func didPan(sender: UIPanGestureRecognizer) {
let velocity = sender.velocity(in: view)
let translation = sender.translation(in: view)
if sender.state == .began {
trayOriginalCenter = sideBarUIView.center
} else if sender.state == .changed {
print("Gesture began")
sideBarUIView.center = CGPoint(x: trayOriginalCenter.x + translation.x, y: trayOriginalCenter.y)
} else if sender.state == .ended {
print("Gesture ended")
if velocity.x > 0 {
UIView.animate(withDuration: 0.3) {
self.sideBarUIView.center = self.sideBarSwipeLeft
}
} else {
UIView.animate(withDuration: 0.3) {
self.sideBarUIView.center = self.siderBarSwipeRight
}
}
}
}
@IBAction func HandleSettings(sender : UIButton) {
print ("Show settings")
}
private func setupLayout(){
// CONSTRAINTS
mainView.heightAnchor.constraint(equalToConstant: screenHeight).isActive = true
mainView.widthAnchor.constraint(equalToConstant: screenWidth).isActive = true
sideBarUIView.heightAnchor.constraint(equalToConstant: screenHeight).isActive = true
sideBarUIView.widthAnchor.constraint(equalToConstant: screenWidth).isActive = true
sideBarUIView.rightAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
}
}
So I figured out the problem. In the sender.state == .ended, I was using self.sideBarUIView.center = self.sideBarSwipeLeft which kept pushing the view up to the center of the screen.
Edit:
I decided to make the hamburger menu from scratch. Taking some ideas from this example: https://github.com/iosapptemplates/drawer-menu-swift
Then I finally went and added some nice touches like if the menu isn't swiped over enough it was hidden away again.
Here is my final code if anyone needs it.
class hamburgerMenu: UIViewController, UIGestureRecognizerDelegate {
let screenHeight = UIScreen.main.bounds.height
let screenWidth = UIScreen.main.bounds.width
var sideBarOriginalCenter: CGPoint!
var mainView: UIView! = {
let mainView = UIView()
mainView.backgroundColor = UIColor(red:0.0, green:1.0, blue:0.0, alpha:1.0)
mainView.translatesAutoresizingMaskIntoConstraints = false
return mainView
}()
var overlayView: UIView! = {
let viewBlack = UIView()
viewBlack.backgroundColor = UIColor(red:0.0, green:0.0, blue:0.0, alpha:1.0)
viewBlack.translatesAutoresizingMaskIntoConstraints = false
return viewBlack
}()
var sideBarUIView: UIView! = {
let sideBarUIView = UIView()
sideBarUIView.backgroundColor = UIColor(red:1.0, green:0.0, blue:0.0, alpha:1.0)
sideBarUIView.translatesAutoresizingMaskIntoConstraints = false
return sideBarUIView
}()
override func viewDidLoad() {
super.viewDidLoad()
let settingsButton = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(HandleSettings))
navigationItem.leftBarButtonItem = settingsButton
view.backgroundColor = UIColor.white
view.addSubview(mainView)
view.addSubview(overlayView)
view.addSubview(sideBarUIView)
overlayView.alpha = 0
let swipeLeftGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(sender:)))
mainView.addGestureRecognizer(swipeLeftGesture)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapOverlay))
mainView.addGestureRecognizer(tapGesture)
setupLayout()
}
@IBAction func didPan(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
if sender.state == .began {
sideBarOriginalCenter = sideBarUIView.center
} else if sender.state == .changed {
sideBarUIView.center = CGPoint(x: sideBarOriginalCenter.x + translation.x, y: sideBarUIView.center.y)
} else if sender.state == .ended {
let negHalfScreenWidth = ((self.screenWidth/2) * -1) / 2 // This should make -187.5 on iphoneX
if sideBarUIView.center.x > negHalfScreenWidth {
UIView.animate(withDuration: 0.3) {
if self.sideBarUIView.center.x > negHalfScreenWidth {
let leftSideOfScreen = self.screenWidth - self.screenWidth
self.sideBarUIView.center = CGPoint(x: leftSideOfScreen ,y: self.sideBarUIView.center.y)
}
}
} else {
UIView.animate(withDuration: 0.3) {
let leftSideOfScreen = (self.screenWidth / 2) * -1
self.sideBarUIView.center = CGPoint(x: leftSideOfScreen ,y: self.sideBarUIView.center.y)
}
}
}
}
@IBAction fileprivate func didTapOverlay() {
UIView.animate(withDuration: 0.3, animations: {
let leftSideOfScreen = (self.screenWidth / 2) * -1
self.sideBarUIView.center = CGPoint(x: leftSideOfScreen ,y: self.sideBarUIView.center.y)
}) { (success) in
}
}
@IBAction func HandleSettings(sender : UIButton) {
if (sideBarUIView.center.x == 0) {
UIView.animate(withDuration: 0.3, animations: {
let leftSideOfScreen = (self.screenWidth / 2) * -1
self.sideBarUIView.center = CGPoint(x: leftSideOfScreen ,y: self.sideBarUIView.center.y)
})
} else if (sideBarUIView.center.x < -0.1) {
UIView.animate(withDuration: 0.3, animations: {
let leftSideOfScreen = self.screenWidth - self.screenWidth
self.sideBarUIView.center = CGPoint(x: leftSideOfScreen ,y: self.sideBarUIView.center.y)
})
}
}
private func setupLayout(){
mainView.heightAnchor.constraint(equalToConstant: screenHeight).isActive = true
mainView.widthAnchor.constraint(equalToConstant: screenWidth).isActive = true
sideBarUIView.rightAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
sideBarUIView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
sideBarUIView.heightAnchor.constraint(equalToConstant: screenHeight).isActive = true
sideBarUIView.widthAnchor.constraint(equalToConstant: screenWidth).isActive = true
overlayView.heightAnchor.constraint(equalToConstant: screenHeight).isActive = true
overlayView.widthAnchor.constraint(equalToConstant: screenWidth).isActive = true
}
}