不期望的 nil 窗口在 _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow 中。

12

我正在尝试在iPad上的iOS 8中设置边缘滑动手势,但是遇到了一个看起来像是错误的问题。

我有以下代码:

    UIScreenEdgePanGestureRecognizer *edgeRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightEdgeSwipe:)];
edgeRecognizer.edges = UIRectEdgeRight;
[self.view addGestureRecognizer:edgeRecognizer];

然后我处理手势:

-(void)handleRightEdgeSwipe:(UIGestureRecognizer*)sender
{
//slide in view code here
}

问题在于它不能每次都正确检测到右边缘滑动,有时会多次检测到。

在 iPad 上滑动右边缘时,无论是否检测到,它总是在控制台中显示以下信息:

2014-10-07 00:04:40.386 Office Log[1531:500896] unexpected nil window in _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow: ; layer = >

这个信息是什么意思,我该如何修复它以便能够一致地检测到右边缘滑动?


@SantaClaus 抱歉,但是你链接的那个问题并不能解决我的问题,因为我已经尝试过了。我的问题不同。 - motionpotion
请参考这个问题。在许多情况下,特别是对于“遗留”应用程序,有一个简单的一键式Interface Builder解决此问题的方法。 - Hot Licks
7个回答

10

我认为这是iOS中的一个错误,在iPad mini 2和iPad Air上的iOS 7或更高版本上,甚至在主屏幕上也可以确认。

在“左横屏”(Home键在左侧)时,从屏幕外部进行“右边缘手势”对我来说无法正常工作。您可以在主屏幕上自行测试。

我9个月前向苹果报告了这个错误,但没有进一步的反应。

更新:

我尝试了一下UIWindow init,当它比实际大小大一点时,手势就可以正常工作了。当然,这是一个可怕的解决方法。

self.window = [UIWindow new];
self.window.rootViewController = [[UIViewController alloc] init];

// Real Size
CGRect frame = [UIScreen mainScreen].bounds;

// Real Size + 0.000001
self.window.frame = CGRectMake(0, 0, frame.size.width+0.000001, frame.size.height+0.000001);
[self.window makeKeyAndVisible];

2
我有完全相同的问题 - 只出现在我的iPad Mini上,运行iOS 7及以上版本 - 我的iPad 3似乎没有遇到同样的问题,但它仍然运行iOS 6... - Richard
@Richard 很有趣。我的测试是在运行iOS 8的iPad 3上完成的。 - motionpotion
请注意,从窗口到附加了屏幕边缘平移手势识别器的视图的整个视图层次结构都需要具有增大的尺寸,否则手势识别器将无法触发。 - Glen Low

8

我遇到了同样的问题。我的解决方案很好用:只需在你的xib中将Windows设置为隐藏。

我不太明白为什么它能起作用,但确实起作用。

编辑1:

我找到了另一个解决方案,我认为更好理解一些:

在你的appDelegate的willFinishLaunchingWithOptions方法中放入以下代码:

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    CGRect bounds = [[UIScreen mainScreen] bounds];
    [self.window setFrame:bounds];
    [self.window setBounds:bounds];

    return YES;
}

接着,在你的didFinishLaunchingWithOptions方法中:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Your codes...

    self.window.rootViewController = self.navigationController;
    [self.window makeKeyAndVisible];

    return YES;
}

你可以将窗口对象的隐藏设置为NO,这样就可以正常工作了。

@Lapiou:你们能否再解释一下,因为我的视图控制器没有xib文件,我该怎么做??? - Paresh Navadiya
@Prince 也许你可以尝试在你的应用程序委托中,在 didFinishLaunchingWithOptions 方法中添加以下代码:self.window.hidden = YES; - Lapinou
找不到对象的“navigationController”。 - RomanKousta

7

我在iPad上测试iPhone应用程序时遇到了问题。在模拟器上没有问题,如果将应用程序编译为通用的并在iPad上运行也没有问题。

unexpected nil window in _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow: <UIClassicWindow: 0x1276065a0; frame = (0 0; 768 1024); userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x1740557e0>; layer = <UIWindowLayer: 0x17403fd80>>

也许框架被错误地报告了?(框架 =(0 0;768 1024))

完全相同的情况 - 尝试在iPad上测试iPhone应用程序,使用2x模式(纯iPhone应用程序,非通用)。 - RomanKousta

3

iOS 8存在一个bug,当iPad处于“倒置竖屏”(Home键在上方)或“左横屏”(Home键在左侧)模式下,任何从正好位于右边缘开始的触摸都无法正确测试到相应的视图。

解决方法是子类化UIWindow以正确测试右侧。

@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

通过 window 属性将窗口附加到您的应用程序代理。

@implementation AppDelegate

- (UIWindow*)window
{
  if (!_window)
    _window = [[IVWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  return _window;
}

@end

在竖屏和右横屏模式下,我已确认右边缘的触摸始终至少离边缘0.5像素而不是恰好在边缘上,因此这个修复方法应该按照类似的方式工作。
扩展窗口框架
请注意,firebug的修复方法也可以使用,即略微扩展窗口框架以包括右侧。但是:
- 如果您在application:willFinishLaunchingWithOptions:或application:didFinishLaunchingWithOptions:中执行此操作,则视图层次结构不会调整为新框架,并且右边缘的触摸将无法通过层次结构。 - 如果您旋转设备,则窗口可能无法正确居中。这会导致界面元素稍微模糊或颠簸。
iOS 7:
iOS 7存在类似的错误,即命中测试也失败,但具有非零结果和未旋转的几何形状。这个修复方法应该适用于iOS 7和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

2

一个可能的解决方法是删除或注释掉隐藏状态栏的代码。我曾经为此苦恼,只在我的根视图上才能重现这个问题。看起来如果你隐藏状态栏,你将不能轻松地向下拖动今天小部件/通知中心。

/* <- add this
- (BOOL)prefersStatusBarHidden
{
   return YES;
}
add this -> */

1
将您的部署设置为8.x或更高版本,将启动屏幕设置为主要xib。
完成!

虽然这理论上回答了问题,但最好在此处包含答案的基本部分,并提供参考链接。 - Enamul Hassan

1
可能已经晚了,但仍有一些人可能需要它。通常的原因是您没有提供正确大小的启动图像或启动屏幕,并且/或者“主界面”未设置为您自己的故事板在“常规”>“部署信息”中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接