iOS 8 has a bug where any touch that begins exactly on an iPad right edge when in Portrait Upside-Down (home button on top) or Landscape Left (home button on left) mode fails to be hit tested to the correct view.
The fix is to subclass UIWindow
to hit test the right side properly.
@implementation FixedWindow
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hit = [super hitTest:point withEvent:event];
if (!hit && point.x >= CGRectGetMaxX(self.bounds))
hit = [super hitTest:CGPointMake(point.x - 0.5, point.y) withEvent:event];
return hit;
}
@end
Attach the window to your application delegate via the window
property.
@implementation AppDelegate
- (UIWindow*)window
{
if (!_window)
_window = [[IVWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
return _window;
}
@end
In Portrait and Landscape Right modes, I've confirmed that right edge touches are always at least 0.5px from the edge instead of exactly on the edge, so this fix should work in analogy to that.
Expanding the window frame
Note that firebug's fix will also work i.e. slightly expanding the window frame to include the right side. However:
- If you do this at
application:willFinishLaunchingWithOptions:
or application:didFinishLaunchingWithOptions:
, your view hierarchy doesn't get resized to the new frame and right edge touches won't make it through the hierarchy.
- If you rotate the device, the window may not be centered correctly. This leads to either smearing or bumping interface elements slightly.
iOS 7:
iOS 7 has a similar bug in that the hit test also fails but with a non-nil result and unrotated geometry. This fix should work with both iOS 7 and 8:
@implementation FixedWindow
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hit = [super hitTest:point withEvent:event];
if (!hit || hit == self)
{
CGRect bounds = self.bounds;
hit = [super hitTest:CGPointMake(MIN(MAX(point.x, CGRectGetMinX(bounds) + 0.5), CGRectGetMaxX(bounds) - 0.5),
MIN(MAX(point.y, CGRectGetMinY(bounds) + 0.5), CGRectGetMaxY(bounds) - 0.5))
withEvent:event];
}
return hit;
}
@end