3 回答
使用 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 版本),因此您可能需要也可能不需要包含此类调用。
我猜想管理/更改第一响应者链是在主循环中以某种方式完成的,当 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:
您需要确保 textField 安装在视图层次结构中。
如果视图的 window 属性包含一个 UIWindow 对象,则它已安装在视图层次结构中;如果它返回 nil,则视图与任何层次结构分离。
希望这会有所帮助....