Search code examples
iosswiftuibuttonhittest

How to get touch event for uibutton outside of another uiwindow


I have my firstVC in the main window and when a button is pressed I animate a second window from the bottom right hand corner that stops almost 3/4 way of the entire screen. Everything animates fine. The problem is I have a cancelButton (red X) on the outside of the second window and when I tap it nothing registers. I know it's outside of it's parent's bounds so I tried a hitTest but still nothing.

func setAnchorsForCancelButton() {

    secondWindow?.addSubview(self.cancelButton)

    cancelButton.bottomAnchor.constraint(equalTo: secondWindow!.topAnchor, constant: -10).isActive = true

    cancelButton.leadingAnchor.constraint(equalTo: secondWindow!.leadingAnchor, constant: 10).isActive = true
    // width and height are 35
}

func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

    let translatedPoint = cancelButton.convert(point, from: secondWindow!.rootViewController!.view)

    if (cancelButton.bounds.contains(translatedPoint)) {
        return cancelButton.hitTest(translatedPoint, with: event)
    }
    return hitTest(point, with: event)
}

The Red X is a cancel button that is on the outside of the second window. It's not receiving the touch events

How can I get it to receive the touch events while still outside of the second window?

enter image description here

code that launches the Second UIWindow:

class SecondWindow: NSObject {

    lazy var cancelButton: UIButton = {
        // button created
    }()

    var secondWindow: UIWindow?
    let webViewVC = WebViewController() // instagram gets shown in here
    let navVC: UINavigationController?

    override init() {
        super.init()
        // some Notifications are in here
    }

    func animateFromBottom() {

        guard let keyWindow = UIApplication.shared.keyWindow else { return }

        let startingFrame = CGRect(x: keyWindow.frame.width - 10,
                                   y: keyWindow.frame.height - 10,
                                   width: 10,
                                   height: 10)

        let endingRect = CGRect(x: 0,
                                y: 150,
                                width: keyWindow.frame.width,
                                height: keyWindow.frame.height)

         let navVC = UINavigationController(rootViewController: webViewVC)

         secondWindow = UIWindow(frame: startingFrame)
         secondWindow?.windowLevel = UIWindow.Level.normal
         secondWindow?.rootViewController = navVC!
         secondWindow?.makeKey()
         secondWindow?.isHidden = false

         // a function with an animation animates the second window to the endingFrame

         setAnchorsForCancelButton()
    }

    func setAnchorsForCancelButton() {

        secondWindow?.addSubview(self.cancelButton)

        cancelButton.bottomAnchor.constraint(equalTo: secondWindow!.topAnchor, constant: -10).isActive = true

        cancelButton.leadingAnchor.constraint(equalTo: secondWindow!.leadingAnchor, constant: 10).isActive = true

        // width and height are 35
    }

    func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

        let translatedPoint = cancelButton.convert(point, from: secondWindow!.rootViewController!.view)

        if (cancelButton.bounds.contains(translatedPoint)) {
            return cancelButton.hitTest(translatedPoint, with: event)
        }
        return hitTest(point, with: event)
    }
}

button from firstVC that launches the second window:

@obj func launchSecondWindow(_ sender: UIButton) {

    let secondWindow = SecondWindow()
    secondWidow.animateFromBottom()
}

Solution

  • I found the answer here and here

    I subclassed UIWindow and did an override to the hitTest inside of there.

    class AnotherWindow: UIWindow {
    
        override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    
            for subview in subviews.reversed() {
    
                let convertedPoint = subview.convert(point, from: self)
    
                if let candidate = subview.hitTest(convertedPoint, with: event) {
    
                    return candidate
                }
            }
    
            return self
        }
    }
    

    Then inside the SecondWindow class I used my subclass instead:

    // this was what I originally used
    var secondWindow: UIWindow?
    
    // **This is what I'm using now**
    var secondWindow: AnotherWindow?