3

I have two UIWindows on the screen and one is behind the other. is there a way to handle the user touch with the window that is behind?

Thanks

Ponja
  • 663
  • 1
  • 8
  • 15

4 Answers4

2

You should just be able to disable it like any UIView.

UIWindow *secondWindow = [[UIWindow alloc] initWithFrame:<#frame#>];
[secondWindow setUserInteractionEnabled:NO];
Noah Witherspoon
  • 57,021
  • 16
  • 130
  • 131
Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
  • That would mean, that neither the upper window nor the window behind will receive any user interaction events, right? – jboi Sep 06 '16 at 17:38
  • 2
    I don't believe so. If you disable user interaction it doesn't eat the touches, but rather ignores it. So your touches should go throw it to the window underneath it. – Ryan Poolos Sep 06 '16 at 18:04
2

iOS looks by default first which window is touched, if multiple windows are in the touch area it picks the top one (based on window level, z index)

next it will call hitTest(_ point: CGPoint, with event: UIEvent) -> UIView? on the window that the os thinks is touched.

If your views in that window might return nil (no view could be touched in that window, or you have overridden the hitTest) then the window will return it self.

To solve this you will need to subclass UIWindow and override the hitTest function. If the view returned from the super.hitTest_:with:) is nil or self // the window then you might choose to delegate to another window. Pay attention to the fact that the window that was touched might have another coordinate space then the window that you will delegate to.

Example:

internal final class CustomWindow: UIWindow {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if let view = super.hitTest(point, with: event), view != self {
            return view
        }

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

        return keyWindow.hitTest(keyWindow.convert(point, from: self), with: event)
    }
}
Saren Inden
  • 3,450
  • 4
  • 32
  • 45
  • Beware, this will not work in ios 13, as it insert some topmost private views in UIWindow, that will catch touches and super.hitTest will return those private view. – Alfred Zien Oct 11 '19 at 10:04
1

You will only be able to accept touch events on one UIWindow at a time. The UIWindow that accepts events is called the keyWindow.

[behindWindow makeKeyAndVisible];

Your foreground UIWindow will remain visible, but your behindWindow will be receiving events.

Ian MacDonald
  • 13,472
  • 2
  • 30
  • 51
  • But then only the key window could receive user interaction, right? What, if I have UI elements on both windows that need interaction? – jboi Sep 06 '16 at 17:39
  • You can only accept touch events on one window at a time. There is always a key window: this is the one and only window that accepts touch events. By using the method shown in this answer, you are telling the system to use a different key window. – Ian MacDonald Sep 06 '16 at 18:12
  • the front windows keep getting the events – user2387149 Oct 07 '16 at 18:44
  • @user2387149 As I stated in my answer (and in a comment response), **you will only be able to accept touch events on one `UIWindow` at a time**. – Ian MacDonald Oct 07 '16 at 19:09
  • @IanMacDonald you can have two separate windows and receive touch events on both of them at the same time even while only 1 remains as the keyWindow: https://stackoverflow.com/a/34835333/4833705 – Lance Samaria Jul 25 '20 at 10:36
  • @LanceSamaria Yes, that’s true (and I really like the answer there), but that solution jumps a couple of hoops to get that to happen. Under normal operation, only the key window observes events. – Ian MacDonald Jul 25 '20 at 23:44
  • You’re right, there are a couple of hoops but honestly the process takes about 10 minutes. He had all that extra stuff for moving the button around to corners, adding it to app delegate etc. Without all that extra stuff it’s easy. Anyway Happy Coding!!! – Lance Samaria Jul 25 '20 at 23:49
1

Thanks, but i just found the way:

  - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
  {
      for (UIWindow *win  in [[UIApplication sharedApplication] windows]) {
          if (win.tag != self.tag) {
              return [win hitTest:point withEvent:event];
          }
      }
      return nil;
  }
Ponja
  • 663
  • 1
  • 8
  • 15