您的视图层次结构位于UIWindow
. UIWindow
负责在其方法中将触摸事件转发到正确的视图sendEvent:
。让我们创建一个UIWindow
to override的子类sendEvent:
。
@interface MyWindow : UIWindow
@end
该窗口将需要对当前第一响应者的引用(如果有的话)。您可能还决定使用UITextView
,因此我们将观察来自文本字段和文本视图的通知。
@implementation MyWindow {
UIView *currentFirstResponder_;
}
- (void)startObservingFirstResponder {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(observeBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
[center addObserver:self selector:@selector(observeEndEditing:) name:UITextFieldTextDidEndEditingNotification object:nil];
[center addObserver:self selector:@selector(observeBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
[center addObserver:self selector:@selector(observeEndEditing:) name:UITextViewTextDidEndEditingNotification object:nil];
}
- (void)stopObservingFirstResponder {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:UITextFieldTextDidBeginEditingNotification object:nil];
[center removeObserver:self name:UITextFieldTextDidEndEditingNotification object:nil];
[center removeObserver:self name:UITextViewTextDidBeginEditingNotification object:nil];
[center removeObserver:self name:UITextViewTextDidEndEditingNotification object:nil];
}
- (void)observeBeginEditing:(NSNotification *)note {
currentFirstResponder_ = note.object;
}
- (void)observeEndEditing:(NSNotification *)note {
if (currentFirstResponder_ == note.object) {
currentFirstResponder_ = nil;
}
}
窗口将在初始化时开始观察通知,并在释放时停止:
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self commonInit];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self commonInit];
}
return self;
}
- (void)commonInit {
[self startObservingFirstResponder];
}
- (void)dealloc {
[self stopObservingFirstResponder];
}
我们将覆盖sendEvent:
以根据事件“调整”第一响应者,然后调用 supersendEvent:
以正常发送事件。
- (void)sendEvent:(UIEvent *)event {
[self adjustFirstResponderForEvent:event];
[super sendEvent:event];
}
如果没有第一响应者,我们不需要对第一响应者做任何事情。如果有第一响应者,并且它包含一个触摸,我们不想强迫它辞职。(记住,可以同时有多个触摸!)如果有第一响应者,并且新的触摸出现在另一个可以成为第一响应者的视图中,系统会自动正确处理,所以我们也想忽略这种情况。但是如果有一个第一响应者,并且它不包含任何触摸,并且一个新的触摸出现在一个不能成为第一响应者的视图中,我们想让第一响应者辞职。
- (void)adjustFirstResponderForEvent:(UIEvent *)event {
if (currentFirstResponder_
&& ![self eventContainsTouchInFirstResponder:event]
&& [self eventContainsNewTouchInNonresponder:event]) {
[currentFirstResponder_ resignFirstResponder];
}
}
在第一响应者中报告事件是否包含触摸很容易:
- (BOOL)eventContainsTouchInFirstResponder:(UIEvent *)event {
for (UITouch *touch in [event touchesForWindow:self]) {
if (touch.view == currentFirstResponder_)
return YES;
}
return NO;
}
在无法成为第一响应者的视图中报告事件是否包含新触摸几乎同样简单:
- (BOOL)eventContainsNewTouchInNonresponder:(UIEvent *)event {
for (UITouch *touch in [event touchesForWindow:self]) {
if (touch.phase == UITouchPhaseBegan && ![touch.view canBecomeFirstResponder])
return YES;
}
return NO;
}
@end
一旦你实现了这个类,你需要改变你的应用程序来使用它而不是UIWindow
.
如果您正在创建UIWindow
in application:didFinishLaunchingWithOptions:
,则需要#import "MyWindow.h"
在您的顶部AppDelegate.m
,然后更改application:didFinishLaunchingWithOptions:
为创建 aMyWindow
而不是 a UIWindow
。
如果您要UIWindow
在 nib 中创建,则需要将窗口的自定义类设置为MyWindow
在 nib 中。