20
4

3 回答 3

28

使用 Xcode 5.1 和XCTestCase,这似乎可以正常工作:

- (void)testFirstResponder
{      
  // Make sure the controller's view has a window
  UIWindow *window = [[UIWindow alloc] init];
  [window addSubview:controller.view];

  // Call whatever method you're testing
  [controller.textView becomeFirstResponder];

  // Assert that the desired subview is the first responder
  XCTAssertTrue([sut.textView isFirstResponder]);
}

为了让视图/子视图成为第一响应者,它必须是视图层次结构的一部分,这意味着必须设置其根视图的窗口属性。

Jon 和 Sergio 提到您可能需要在调用所需的子视图[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]后调用becomeFirstResponder,但我发现这在我们的实例中不是必需的。

但是,您的里程可能会有所不同(甚至取决于您使用的 Xcode 版本),因此您可能需要也可能不需要包含此类调用。

于 2014-06-11T07:46:27.460 回答
9

我猜想管理/更改第一响应者链是在主循环中以某种方式完成的,当 UI 更新为下一个事件处理做准备时。如果这个假设是正确的,我会简单地做以下事情:

-(void)assertIfNotFirstResponder:(UITextField*)field {
    STAssertTrue([field isFirstResponder], nil);
}

- (void)testFirstResponder
{
     [controller view];
     [[controller firstTextField] becomeFirstResponder];
     [self performSelector:@selector(@"assertIfNotFirstResponder:") withObject:[controller firstTextField] afterDelay:0.0];
 }

注意:我使用了 0.0 延迟,因为我只是希望将消息放入事件队列并尽快分派。我只需要一种方法来回到主循环,以进行内务处理。在您的情况下,这应该不会产生实际的延迟。如果您正在执行多个相同类型的测试,即通过重复更改作为第一响应者的控件,则此技术应保证所有这些事件与生成的事件正确排序performSelector

如果您从不同的线程运行测试,您可以使用– performSelectorOnMainThread:withObject:waitUntilDone:

于 2011-05-28T10:23:30.033 回答
7

您需要确保 textField 安装在视图层次结构中。

如果视图的 window 属性包含一个 UIWindow 对象,则它已安装在视图层次结构中;如果它返回 nil,则视图与任何层次结构分离。

希望这会有所帮助....

于 2011-05-28T09:39:01.673 回答