Background

Hamburger menus are traditionally an Android thing, but often a client wants Android and iOS to look the same, and sometimes that means iOS implements a Hamburger menu. There are many different ways to skin this cat. I think the simplest and best way is with a custom transition and a normal push segue over the current context. I’ll show you what I mean.

Download Sample Project on GitHub

MenuViewController

Create a MenuViewController with a sidebar, whatever buttons you want, and a background view constrained far offscreen to the right. Set that background view to be black and alpha 0, and then connect an @IBOutlet . As the push segue happens and we fade in the background, we want the “fade view” to cover the entire width.

Connect a UITapGestureRecognizer to the BackgroundView with an @IBAction to close.

class MenuViewController : UIViewController { @IBOutlet weak var fadeOutView : UIView ! @IBAction func close ( _ sender : Any ) { dismiss ( animated : true , completion : nil ) UIView . animate ( withDuration : 0.25 ) { self . fadeOutView . alpha = 0.0 } } override func viewWillAppear ( _ animated : Bool ) { UIView . animate ( withDuration : 0.25 ) { self . fadeOutView . alpha = 0.75 } } }

SocialViewController

Create a SocialViewController with a button connected to an @IBAction .

In the @IBAction instantiate MenuViewController and set transitioningDelegate to self .

@IBAction func menu ( _ sender : UIButton ) { let storyboard = UIStoryboard ( name : "Main" , bundle : nil ) guard let menuController = storyboard . instantiateViewController ( withIdentifier : "MenuViewController" ) as? MenuViewController else { return } menuController . transitioningDelegate = self present ( menuController , animated : true , completion : nil ) }

Conform to UIViewControllerTransitioningDelegate and add a SlideOverAnimator() .

class SocialViewController : UIViewController , UIViewControllerTransitioningDelegate { let transition = SlideOverAnimator ()

SlideOverAnimator

Create a new class SlideOverAnimator .

import UIKit class SlideOverAnimator : NSObject , UIViewControllerAnimatedTransitioning { let duration = 0.3 var presenting = true func transitionDuration ( using transitionContext : UIViewControllerContextTransitioning ?) -> TimeInterval { return duration } func animateTransition ( using transitionContext : UIViewControllerContextTransitioning ) { let containerView = transitionContext . containerView let animatingView = transitionContext . view ( forKey : . to ) ?? transitionContext . view ( forKey : . from ) ! if presenting { animatingView . transform = CGAffineTransform ( translationX : - animatingView . frame . size . width , y : 0 ) } else { animatingView . transform = CGAffineTransform ( translationX : 0 , y : 0 ) } containerView . addSubview ( animatingView ) UIView . animate ( withDuration : duration , delay : 0.0 , options : . curveEaseInOut , animations : { if self . presenting { animatingView . transform = CGAffineTransform ( translationX : 0 , y : 0 ) } else { animatingView . transform = CGAffineTransform ( translationX : - animatingView . frame . size . width , y : 0 ) } }, completion : { _ in transitionContext . completeTransition ( true ) }) } }

And you’re done.

Download Sample Project on GitHub. MIT License.