Search code examples
iosswiftswift3uitapgesturerecognizer

Gesture recognizer not responding


I have a button on my view controller that brings in a menu and a slightly transparent view blackView behind it - the menu class includes a tap gesture recognizer to dismiss the menu when blackView is tapped. However it's not responding, and I'm not sure where my problem is. Here's the code for the menu:

class MenuController: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

let blackView = UIView()

let collectionView: UICollectionView = {

    let layout = UICollectionViewFlowLayout()
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
    cv.backgroundColor = UIColor.white
    return cv
}()

let cellId = "cellId"


func presentMenu() {

    if let window = UIApplication.shared.keyWindow {

        blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
        blackView.isUserInteractionEnabled = true
        // Tap outside to dismiss
        blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))

        window.addSubview(blackView)
        window.addSubview(collectionView)

        let height: CGFloat = 200
        let y = window.frame.height - height
        collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)

        blackView.frame = window.frame
        blackView.alpha = 0

        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
            self.blackView.alpha = 1

            self.collectionView.frame = CGRect(x: 0, y: y, width: self.collectionView.frame.width, height: self.collectionView.frame.height)

            }, completion: nil)
    }
}


func handleDismiss() {
    print("working")

    UIView.animate(withDuration: 0.5) {
        self.blackView.alpha = 0
        if let window = UIApplication.shared.keyWindow {

            self.collectionView.frame = CGRect(x: 0, y: window.frame.height, width: self.collectionView.frame.width, height: self.collectionView.frame.height)

        }
    }
}


// Collection view data sources
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 3
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
    return cell
}



override init() {
    super.init()

    collectionView.dataSource = self
    collectionView.delegate = self

    collectionView.register(MenuCell.self, forCellWithReuseIdentifier: cellId)
}



}

presentMenu is called and the menu appears when I tap the necessary button on my view controller. But handleDismiss is not getting called. Thanks for any help hopefully this is just a beginner mistake!

EDIT after solution: I was initializing menuController() in my view controller like so:

func showMenu() {
    let menuController = MenuController()

    menuController.presentMenu()

}

And changing it to @ronatory's solution of

lazy var menuController: MenuController = {
  let menuController = MenuController()
  return menuController
}()

func showMenu() {
  menuController.presentMenu()
}

Solved it.


Solution

  • So the only way I found out was to change the initialization of your menuController. Instead of initializing it in the showMenu() method (the problem is that you initialize it in the method and after the method is executed menuController is nil):

    func showMenu() {
        let menuController = MenuController()
        menuController.presentMenu()
    }
    

    Replace it with using a lazy stored property (to also have access to the instance after the method is executed) and there is also no need to set blackView.isUserInteractionEnabled = true. You can delete it:

    lazy var menuController: MenuController = {
      let menuController = MenuController()
      return menuController
    }()
    
    func showMenu() {
      menuController.presentMenu()
    }
    

    That's all. After this change your tap gesture recognizer should work. I tested it with your project. It would be nice if you add this part of your project to the question as code, so others can understand better the problem without needing to download the project