Skip to main content

IOS Creating a Slide Down Menu Using View Controller Transition

Slide Down Menu

Before showing you how to implement the menu, this tutorial assumes that you have a basic understanding of custom view controller transition. For those who are new to view controller transition,.

Okay, let’s get started.

A Quick Demo of Slide Down Menu

In this tutorial, we will build a slide down menu in Swift.

The Project Template

As usual, I don’t want you to start from scratch. One is for the main screen (embedded in a navigation controller) and the other is for the navigation menu. If you run the project, the app should present you the main interface with some dummy data.

Before moving on, take a few minutes to browse through the code template to familiarise yourself with the project.

Presenting the Menu Modally

First, open the Main.storyboard file. You should find two table view controllers, which are not connected with any segue yet. In order to bring up the menu when a user taps the menu button, control-drag from the menu button to the menu table view controller. Release the buttons and select “present modally” under action segue.

If you run the project now, the menu will be presented as a modal view. In order to dismiss the menu, we will add an unwind segue.

Open the NewsTableViewController.swift file and insert an unwind action method:

@IBAction func unwindToHome(segue: UIStoryboardSegue) {

         let sourceController = segue.sourceViewController as! MenuTableViewController
        self.title = sourceController.currentItem
}

Next, go to the storyboard. Control-drag from the prototype cell of the Menu table view controller to the exit icon. When prompted, select the unwindToHome: option under selection segue.

Now when a user taps any menu item, the menu controller will dismiss to reveal the main screen. Through the unwindToHome: action method, the main view controller (i.e. NewsTableViewController) retrieves the menu item selected by the user and changes its title accordingly. To keep things simple, we just change the title of the navigation bar and will not alter the content of the main screen.

In addition, the menu controller will highlight the currently selected item in white. There are a couple of methods we need to implement before the app can work as expected.

Insert the following method in the MenuTableViewController class:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

            let menuTableViewController = segue.sourceViewController as! MenuTableViewController
            if let selectedRow = menuTableViewController.tableView.indexPathForSelectedRow()?.row
           {
               currentItem = menuItems[selectedRow]
           }
}

Here, we just set the currentItem to the selected menu item.

In the NewsTableViewController.swift file, insert the following method to pass the current title to the menu controller:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

      let menuTableViewController = segue.destinationViewController as! MenuTableViewController
      menuTableViewController.currentItem = self.title!
}

Now compile and run the project. Tap the menu item and the app will present you the menu modally. When you select a menu item, the menu will dismiss and the navigation bar title will change accordingly.

Creating the animated Slide Down Menu

Now that the menu is presented using the standard animation, let’s begin to create a custom transition. As I mentioned in the previous chapter, the core of custom view controller animation is the animator object, which conforms to both UIViewControllerAnimatedTransitioning and UIViewControllerTransitioningDelegate protocols.

We are going to implement the class, but first, let’s take a look at how the slide down menu works. When a user taps the menu, the main view begins to slide down until it reaches the predefined location, which is 150 points away from the bottom of the screen. The below illustration should give you a better idea of the sliding menu.

Building the Slide Down Menu Animator

To create the slide down effect, we will create a slide down animator called MenuTransitionManager. In the project navigator, right click to create a new file. Name the class MenuTransitionManager and set it as a subclass of NSObject.

Update the class like this:

class MenuTransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate 
{
      var duration = 0.5
      var isPresenting = false
      var snapshot:UIView?

     func transitionDuration(transitionContext: UIViewControllerContextTransitioning) ->                                 NSTimeInterval 
     {
         return duration
     }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) 
      {
          // Get reference to our fromView, toView and the container view
          let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
          let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!

         // Set up the transform for sliding
         let container = transitionContext.containerView()
         let moveDown = CGAffineTransformMakeTranslation(0, container.frame.height - 150)
         let moveUp = CGAffineTransformMakeTranslation(0, -50)

        // Add both views to the container view
        if isPresenting {

        toView.transform = moveUp
        snapshot = fromView.snapshotViewAfterScreenUpdates(true)
        container.addSubview(toView)
        container.addSubview(snapshot!)
  }
  
     // Perform the animation
     UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.9,                                    initialSpringVelocity: 0.3, options: nil, animations: {

    if self.isPresenting {

    self.snapshot?.transform = moveDown
    toView.transform = CGAffineTransformIdentity

    } else {

  self.snapshot?.transform = CGAffineTransformIdentity
  fromView.transform = moveUp
 }

 }, completion: { finished in

    transitionContext.completeTransition(true)
    if !self.isPresenting {
    self.snapshot?.removeFromSuperview()
   }
 })
}

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

    isPresenting = false
    return self
}

func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

    isPresenting = true
    return self
  }
}

The class implements both UIViewControllerAnimatedTransitioning and UIViewControllerTransitioningDelegate protocols. I will not go into the details of the methods, as they are explained in the previous chapter. Let’s focus on the animation block (i.e. the animateTransition method).

Referring to the illustration displayed earlier, during the transition, the main view is the fromView, while the menu view is the toView.

To create the animations, we configure two transforms. The first transform (i.e. moveDown) is used to move down the main view. The other transform (i.e. moveUp) is configured to move up the menu view a bit so that it will also have a slide-down effect when restoring to its original position. You will understand what I mean when you run the project later.

From iOS 7 and onwards, you can use the UIView-Snapshotting API to quickly and easily create a light-weight snapshot of a view.

     snapshot = fromView.snapshotViewAfterScreenUpdates(true)

By calling the snapshotViewAfterScreenUpdates method, you have a snapshot of the main view. With the snapshot, we can add it to the container view to perform the animation. Note that the snapshot is added on top of the menu view.

For the actual animation when presenting the menu, the implementation is really simple. We just apply the moveDown transform to the snapshot of the main view and restore the menu view to its default position.

     self.snapshot?.transform = moveDown
     toView.transform = CGAffineTransformIdentity

When dismissing the menu, the reverse happens. The snapshot of the main view slides up and returns to its default position. Additionally, the snapshot is removed from its super view so that we can bring the actual main view back.

Now open NewsTableViewController.swift and declare a variable for the MenuTransitionManager object:

     var menuTransitionManager = MenuTransitionManager()

In the prepareForSegue method, add a line of code to hook up the animation:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

     let menuTableViewController = segue.destinationViewController as! MenuTableViewController
     menuTableViewController.currentItem = self.title!
     menuTableViewController.transitioningDelegate = self.menuTransitionManager
}

That’s it! You can now compile and run the project. Tap the menu button and you will have a slide down menu.

Detecting Tap Gesture

For now, the only way to dismiss the menu is to select a menu item. From a user’s perspective, tapping the snapshot should dismiss the menu too. However, the snapshot of the main view is non-responsive.

The snapshot is actually a UIView object. So we can create a UITapGestureRecognizer object and add it to the snapshot.

When instantiating a UITapGestureRecognizer object, we need to pass it the target object that is the recipient of action messages sent by the receiver, and the action method to be called. Obviously, you can hardcode a particular object as the target object to dismiss the view, but to keep our design flexible, we will define a protocol and let the delegate object implement it.

In MenuTransitionManager.swift, define the following protocol:

  @objc protocol MenuTransitionManagerDelegate {
          func dismiss()
   }

Here we define a MenuTransitionManagerDelegate protocol with a required method. The delegate should implement the dismiss method and provide the actual logic for dismissing the view.

In the MenuTransitionManager class, declare a delegate variable:

     var delegate:MenuTransitionManagerDelegate?

Later, the object which is responsible to handle the tap gesture should be set as the delegate object.

Lastly, we need to create a UITapGestureRecognizer object and add it to the snapshot. A good way to do this is define a didSet method within the snapshot variable. Change the snapshot declaration to the following:

var snapshot:UIView? {

     didSet {
     if let _delegate = delegate {
     let tapGestureRecognizer = UITapGestureRecognizer(target: _delegate, action: "dismiss")
     snapshot?.addGestureRecognizer(tapGestureRecognizer)
    }
   }
}

Property observer is one of the powerful features in Swift. The observer (willSet/didSet) is called every time a property’s value is set. This provides us a convenient way to perform certain actions immediately before or after an assignment. The willSet method is called right before the value is stored, while the didSet method is called immediately after the assignment.

In the above code, we make use of the property observer to create a gesture recogniser and set it to the snapshot. So every time we assign the snapshot variable an object, it will immediately configure with a tap gesture recogniser.

We are almost done. Now go back to NewsTableViewController.swift, which is the class to implement the MenuTransitionManagerDelegate protocol.

First, change the class declaration to the following:

     class NewsTableViewController: UITableViewController, MenuTransitionManagerDelegate

Next, implement the required method of the protocol:

func dismiss() {

    dismissViewControllerAnimated(true, completion: nil)
}

Here, we simply dismiss the view controller by calling the dismissViewControllerAnimated method.

Lastly, insert a line of code in the prepareForSegue method of the NewsTableViewController class to set itself as the delegate object:

     self.menuTransitionManager.delegate = self

Great! You’re now ready to test the app again. Hit the Run button to try it out. You should be able to dismiss the menu by tapping the snapshot of the main view.

By applying custom view controller transitions properly, you can greatly improve the user experience and set your app apart from the crowd.


Comments

Popular Posts

What are the Alternatives of device UDID in iOS? - iOS7 / iOS 6 / iOS 5 – Get Device Unique Identifier UDID

Get Device Unique Identifier UDID Following code will help you to get the unique-device-identifier known as UDID. No matter what iOS user is using, you can get the UDID of the current iOS device by following code. - ( NSString *)UDID { NSString *uuidString = nil ; // get os version NSUInteger currentOSVersion = [[[[[UIDevice currentDevice ] systemVersion ] componentsSeparatedByString: @" . " ] objectAtIndex: 0 ] integerValue ]; if (currentOSVersion <= 5 ) { if ([[ NSUserDefaults standardUserDefaults ] valueForKey: @" udid " ]) { uuidString = [[ NSUserDefaults standardDefaults ] valueForKey: @" udid " ]; } else { CFUUIDRef uuidRef = CFUUIDCreate ( kCFAllocatorDefault ); uuidString = ( NSString *) CFBridgingRelease ( CFUUIDCreateString ( NULL ,uuidRef)); CFRelease (uuidRef); [[ NSUserDefaults standardUserDefaults ] setObject: uuidString ForKey: @" udid " ]; [[ NSUserDefaults standardUserDefaults ] synchro...

16 AWS Gotchas

16 AWS Gotchas In January I launched the MVP for my own startup,  Proximistyle , which helps you find what you’re looking for nearby. On advice from friends and industry contacts I chose AWS as my cloud provider. Having never had to set up my own cloud infrastructure before, the learning curve to get from no experience to a stable VPC system I was happy with was significantly steeper than expected, and had its fair share of surprises. #1 Take advantage of the free resources offered AWS offers a free tier for new accounts. If you have recently bought a domain and set up a company you qualify for the free tier for a year. Additionally, if you are a bootstrapped startup you can apply for  the Startup Builders package  and get $1000 in AWS credits. After doing the above, you’re now ready to get started with setting up the AWS infrastructure for your startup. #2 Set up billing budgets and alerting The very first thing you should do after setting up billing, is enabling a budge...

Ultimate Folder Structure For Your React Native Project

  Ultimate Folder Structure For Your React Native Project React native project structure React Native is a flexible framework, giving developers the freedom to choose their code structure. However, this can be a double-edged sword for beginners. Though it offers ease of coding, it can soon become challenging to manage as your project expands. Thus, a structured folder system can be beneficial in many ways like better organization, simplified module management, adhering to coding practices, and giving a professional touch to your project. This write-up discusses a version of a folder arrangement that I employ in my React Native projects. This structure is based on best practices and can be modified to suit the specific needs of your project. Before we get into the project structure let’s give credit to @sanjay who has the original idea of the structure but I modify his version of the code, to make it better. Base library axios  — For network calling. react-navigation ...