I have two windows -- a front one, and a back one. The front one is used to overlay some stuff on top of the back one. I want to be able to capture touches on some parts of the front window, but not others; I want the front window to receive touches in some areas, but pass them through to the back window in others.
Any ideas as to how I'd do this?
Ok, here's what I did: I created two views in the front window. The first view covered the area where I wanted to catch the touches; the second, where I wanted the touches to pass through. I sub-classed UIWindow and overrode the hitTest:withEvent method like so:
- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// See if the hit is anywhere in our view hierarchy
UIView *hitTestResult = [super hitTest:point withEvent:event];
// ABKSlideupHostOverlay view covers the pass-through touch area. It's recognized
// by class, here, because the window doesn't have a pointer to the actual view object.
if ([hitTestResult isKindOfClass:[ABKSlideupHostOverlayView class]]) {
// Returning nil means this window's hierachy doesn't handle this event. Consequently,
// the event will be passed to the host window.
return nil;
}
return hitTestResult;
}
And in the class which creates the front window, I used a gesture recognizer to catch touches on the first view.
Same in 2017 code:
class PassView: UIView {}
class UIHigherWindow: UIWindow {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if hitView!.isKind(of: PassView.self) {
return nil
}
return hitView
}
}
Your "uber" window will be some view controller, UberVc.
Just make the main view (that is to say, simply the background .view) of UberVc be a PassView.
Then add say buttons etc. on the UberVc.
The above code results in any clicks on the buttons going to UberVc, and any clicks not on the buttons (ie, on the "background") going through to your regular window/VCs.
I had a similar issue: the additional UIWindow should have a transparent view on it with some intractable subviews. All touches outside of those intractable views must be passed through.
I ended up using modified version of Anna's code that uses view's tag instead of type checking. This way view subclass creation is not required:
class PassTroughWindow: UIWindow {
var passTroughTag: Int?
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if let passTroughTag = passTroughTag {
if passTroughTag == hitView?.tag {
return nil
}
}
return hitView
}
}
Assuming you have a window and root view controller created you you could use it like this:
let window: PassTroughWindow
//Create or obtain window
let passTroughTag = 42
window.rootViewController.view.tag = passTroughTag
window.passTroughTag = passTroughTag
//Or with a view:
let untouchableView: UIView // Create it
untouchableView.tag = passTroughTag
window.addSubview(untouchableView)
Instead of using multiple UIWindows, get the key window with [[UIApplication sharedApplication] keyWindow]
, then add your view to it:
mainWindow = [[UIApplication sharedApplication] keyWindow];
[mainWindow addSubview:view]