你的视图层次结构位于UIWindow
. The UIWindow
负责将触摸事件转发到其正确的视图sendEvent:
方法。让我们创建一个子类UIWindow
覆盖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:
根据事件“调整”第一响应者,然后调用超级的sendEvent:
正常发送事件。
- (void)sendEvent:(UIEvent *)event {
[self adjustFirstResponderForEvent:event];
[super sendEvent:event];
}
如果没有第一响应者,我们不需要对第一响应者做任何事情。如果有第一响应者,并且它包含触摸,我们不想强迫它辞职。 (请记住,可以同时有多个触摸!)如果有第一响应者,并且新的触摸出现在可以成为第一响应者的另一个视图中,系统将自动正确处理该情况,因此我们也想忽略这种情况。但是,如果有第一响应者,并且它不包含任何触摸,并且新触摸出现在视图中,can't成为第一响应者,我们想让第一响应者辞职。
- (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:
创建一个MyWindow
代替UIWindow
.
如果您正在创建您的UIWindow
在笔尖中,您需要将窗口的自定义类设置为MyWindow
在笔尖。