     let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapGesture(gesture:)))
     tapGesture.numberOfTapsRequired = 1
     tapGesture.numberOfTouchesRequired = 1
+    tapGesture.delegate = self
     sideMenuContainerView.addGestureRecognizer(tapGesture)
   }
   


import UIKit

public final class LNSideMenu: NSObject, UIGestureRecognizerDelegate {
  
  public typealias Completion = () -> ()
  

    }
  }
  
  internal func handleGesture(_ gesture: UISwipeGestureRecognizer) {
    // Toggle side menu by swipe gesture direction
    cacheEnableDynamic = false
    toggleMenu((position == .right && gesture.direction == .left) || (position == .left && gesture.direction == .right))
  }

  internal func tapGesture(gesture: UIGestureRecognizer) {
    guard tapOutsideToDismiss else { return }
    if let custom = customMenu?.view {
      if !custom.frame.contains(gesture.location(in: sideMenuContainerView)) {
        toggleMenu()
      }
    } else {
      toggleMenu()
    }
  }
  
  // MARK: Animations
  
  fileprivate func animateSideMenu(_ isShow: Bool, animation: Bool = true, completion: Completion? = nil) {
    // Do nothing if the menu was disabled
    if disabled || !dynamicAnimatorEnded { return }
    // Do nothing if the expected
    if isShow && delegate?.sideMenuShouldOpenSideMenu?() == false {
      return
    }
    // Should update if needed
    updateSideMenuApperanceIfNeeded()
    isMenuOpen = isShow
    let (width, height) = adjustFrameDimensions |> (sourceView.frame.width, sourceView.frame.height)
    // Check if it's allowed to performing dynamic animation
    if cacheEnableDynamic {
      // Change ended anymator flag to false before starting dynamic animator
      dynamicAnimatorEnded = false
      // Delegate sidemenu will open/close
      isShow ? delegate?.sideMenuWillOpen?() : delegate?.sideMenuWillClose?()
      // If bouncing enabled is true, performing dynamic behavior instead of standard animation
      dynamicBehavior(isShow, width: width, height: height)
      // Starting show/hide blur view animation
      animateBlurview(isShow: isShow)
      return
    }
    animationCompleted = false
    // Preparing for animating menu's contents
    var duration = animationDuration
    if isShow && animation && !isCustomMenu {
      menuViewController?.prepareForAnimation()
      duration = shortDuration
    }
    // Calculating destination sidemenu frame based on the menu position and isShow param
    let destFrame = position == .left ? CGRect(x: isShow ? 0: -menuWidth, y: ypos, width: menuWidth, height: height) :
      CGRect(x: isShow ? width-menuWidth : width, y: ypos, width: menuWidth, height: height)
    // Performing a standard animation
    let closure = {
      self.isMenuOpen ? self.delegate?.sideMenuDidOpen?() : self.delegate?.sideMenuDidClose?()
      // Begins animating menu's contents
      if let completion = completion { completion() }
      self.animationCompleted = true
    }
    UIView.animate(withDuration: duration, animations: {
      self.sideMenuContainerView.frame = destFrame
      }, completion: { _ in
        if !(isShow && animation && !self.isCustomMenu) { closure() }
    })
    if isShow && animation && !isCustomMenu {
      // Performing menu's contents fade animation
      menuViewController?.animateContents() {
        closure()
      }
    }
    // Starting show/hide blur view animation
    animateBlurview(isShow: isShow)
    isShow ? delegate?.sideMenuWillOpen?() : delegate?.sideMenuWillClose?()
  }
  
  fileprivate func animateBlurview(isShow: Bool = false, byOffset offset: (Bool, CGFloat)? = nil) {
    if !isCustomMenu { return }
    var alpha: CGFloat = isShow ? 1: 0
    // Calculating alpha val by offset if required
    if let offset = offset, offset.0 {
      alpha = offset.1 / (sideMenuContainerView.width/2)
      alpha = 0...1 ~= alpha ? alpha : alpha < 0 ? 0 : 1
    }
    // Animating show/hide blur view by alpha
    UIView.animate(withDuration: animationDuration) {
      self.blurView.alpha = alpha
    }
  }
  
  fileprivate func dynamicBehavior(_ shouldOpen: Bool, width: CGFloat, height: CGFloat) {
    
    // Once this function is called then we must clean up UIDynamicAnimator object
    animator.removeAllBehaviors()
    
    var gravityDirectionX: CGFloat
    var pushMagnitude: CGFloat
    var boundaryPointX: CGFloat
    var boundaryPointY: CGFloat
    
    if (position == .left) {
      // Left side menu
      gravityDirectionX = (shouldOpen) ? kGravityDirection : -kGravityDirection
      pushMagnitude = (shouldOpen) ? kPushMagnitude : -kPushMagnitude
      boundaryPointX = (shouldOpen) ? menuWidth : -menuWidth-2
      boundaryPointY = kDBXPoint
    }
    else {
      // Right side menu
      gravityDirectionX = (shouldOpen) ? -kGravityDirection : kGravityDirection
      pushMagnitude = (shouldOpen) ? -kPushMagnitude : kPushMagnitude
      boundaryPointX = (shouldOpen) ? width-menuWidth : width+menuWidth+2
      boundaryPointY =  -kDBXPoint
    }
    /*
     A UIGravityBehavior object applies a gravity-like force to all of its associated dynamic items. The magnitude and direction of the gravity force are configurable and are applied equally to all associated items. Use this behavior to modify the position of views and other dynamic items in your app’s interface.
     Refer to https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGravityBehavior_Class/ for more details
    */
    let gravityBehavior = UIGravityBehavior(items: [sideMenuContainerView])
    gravityBehavior.gravityDirection = CGVector(dx: gravityDirectionX, dy: 0)
    animator.addBehavior(gravityBehavior)
    
    /*
     A collision behavior confers, to a specified array of dynamic items, the ability of those items to engage in collisions with each other and with the behavior’s specified boundaries.
     For more details: https://developer.apple.com/reference/uikit/uicollisionbehavior
    */
    let collisionBehavior = UICollisionBehavior(items: [sideMenuContainerView])
    collisionBehavior.addBoundary(withIdentifier: "menuBoundary" as NSCopying, from: CGPoint(x: boundaryPointX, y: boundaryPointY), to: CGPoint(x: boundaryPointX, y: height))
    animator.addBehavior(collisionBehavior)
    
    /*
     A push behavior applies a continuous or instantaneous force to one or more dynamic items, causing those items to change position accordingly.
     For more details: https://developer.apple.com/reference/uikit/uipushbehavior
     */
    let pushBehavior = UIPushBehavior(items: [sideMenuContainerView], mode: UIPushBehaviorMode.instantaneous)
    pushBehavior.magnitude = pushMagnitude
    animator.addBehavior(pushBehavior)
    
    /*
     A dynamic item behavior represents a base dynamic animation configuration for one or more dynamic items. Each of its properties overrides a corresponding default value.
     For more details: https://developer.apple.com/reference/uikit/uidynamicitembehavior
     */
    let menuViewBehavior = UIDynamicItemBehavior(items: [sideMenuContainerView])
    menuViewBehavior.elasticity = 0.15
    animator.addBehavior(menuViewBehavior)
  }
  
  // MARK: Toggle Menu
  
  fileprivate func panToogleMenu(_ show: Bool) {
    var centerx = sourceView.center.x
    if !show {
      centerx = position == .left ? -sourceView.width/2 : sourceView.width + sourceView.width/2
    }
    isMenuOpen = show
    show ? delegate?.sideMenuWillOpen?() : delegate?.sideMenuWillClose?()
    UIView.animate(withDuration: animationDuration, animations: {
      self.sideMenuContainerView.center.x = centerx
    }) { _ in
      self.animationCompleted = true
      show ? self.delegate?.sideMenuDidOpen?() : self.delegate?.sideMenuDidClose?()
    }
    // Starting show/hide blur view animation
    animateBlurview(isShow: show)
  }
  
  fileprivate func toggleMenu(_ isShow: Bool, completion: Completion? = nil) {
    // Waiting until all animations are done
    if animationCompleted {
      let animation = isShow && self.isMenuOpen ? false : self.enableAnimation
      self.animateSideMenu(isShow, animation: animation, completion: completion)
    }
  }
  
  open func toggleMenu(completion: Completion? = nil) {
    // Revert boucingEnabled default value, because it might be changed by gesture handlers
    cacheEnableDynamic = isMenuOpen ? false : enableDynamic
    toggleMenu |> (!isMenuOpen, completion)
    if !isMenuOpen { updateSideMenuApperanceIfNeeded() }
  }
  
  open func hideSideMenu(_ completion: Completion? = nil) {
    // disable dynamic animator when hiding sidemenu
    cacheEnableDynamic = false
    // Just only hide sidemenu if it was really shown
    if isMenuOpen { toggleMenu |> (false, completion) }
  }
  
  open func showSideMenu(completion: Completion? = nil) {
    // Revert boucingEnabled default value, because it might be changed by gesture handlers
    cacheEnableDynamic = enableDynamic
    // Just only show sidemenu if it was really hidden
    if !isMenuOpen { toggleMenu |> (true, completion) }
  }
  
  // MARK: Frame and refresh content
  
  open func refreshMenu(_ items: [String]) {
    // Refresh side menu with a new list items
    self.items = items
    if !isCustomMenu {
      menuViewController?.sideMenuView.items = items
      menuViewController?.sideMenuView.refresh()
    }
  }
  
  internal func updateFrame() {
    // Get adjusted frame dimensions that is dependent on ios version and device oriented
    let (width, height) = adjustFrameDimensions |> (sourceView.frame.width, sourceView.frame.height)
    // Re-assign container view frame based on menu position after getting adjusted frame dimensions
    sideMenuContainerView.frame = CGRect(x: position == .left ? isMenuOpen ? 0: -menuWidth : isMenuOpen ? width - menuWidth : width+1
      , y: ypos, width: width, height: height)
    // Update blur view y position
    if isCustomMenu {
      blurView.y = ypos - 20
    }
  }
  
  // Implementing set content viewcontroller effection
  fileprivate func transitionContentVC(completion: Completion) {
    // TODO:
  }
  
  fileprivate func refreshSideMenu() {
    // Refresh side menu by updating its frame
    updateFrame()
    // Handle navigation bar translucent if needed
    if !isCustomMenu {
      menuViewController?.isNavigationBarChanged = isNavbarHiddenOrTransparent
    }
  }
  
  fileprivate func adjustFrameDimensions( _ width: CGFloat, height: CGFloat ) -> (CGFloat,CGFloat) {
    return (width, height-ypos)
  }
}

// MARK: LNSMDelegate

extension LNSideMenu: LNSMDelegate {
  func didSelectItemAtIndex(SideMenu: LNSideMenuView, index: Int) {
    // Handle if dynamic animator is in processing
    if !dynamicAnimatorEnded {
      dispatch_group.notify(queue: DispatchQueue.main, execute: { [weak self] in
        self?.handleDidSelectItemAtIndex(index)
      })
    } else {
      handleDidSelectItemAtIndex(index)
    }
  }
  
  fileprivate func handleDidSelectItemAtIndex(_ index: Int) {
    // Hide side menu if needed
    if hideWhenDidSelectOnCell {
      // Hide Sidemenu and perform a closure then
      hideSideMenu({ [unowned self] in
        // Calling delegate method after hiding sidemenu
        self.delegate?.didSelectItemAtIndex(index)
        })
    } else {
      // Send back did select item at index action by delegate
      delegate?.didSelectItemAtIndex(index)
    }
  }
}

// MARK: Dynamic animator delegate

extension LNSideMenu: UIDynamicAnimatorDelegate {
  public func dynamicAnimatorDidPause(_ animator: UIDynamicAnimator) {
    // Flag for dynamic animator did end animation
    dynamicAnimatorEnded = true
    // Release current dynamicAnimator task in dispatch group
    dispatch_group.leave()
    // Calling delegate after dynamic animator did pause, 
    // in other word, the toogle sidemenu animation has already completed
    isMenuOpen ? delegate?.sideMenuDidOpen?() : delegate?.sideMenuDidClose?()
  }
  
  public func dynamicAnimatorWillResume(_ animator: UIDynamicAnimator) {
    // Enter dynamicAnimator task to main group
    dispatch_group.enter()
  }
}

extension LNSideMenu: UIGestureRecognizerDelegate {

  public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    print("completed: \(animationCompleted)")
    // Disable gesture if dynamic animator has not ended animation yet
