在UITextField之外的任何地方触摸键盘

wha*_*ean 27 cocoa-touch uitextfield ios

我正在开发具有大量的iPad应用程序UIViewControllers,UITableViews(与细胞accessoryViewsUITextFields)等等,等等很多的UIViewControllers导航层次结构中出现.

有很多不同的地方UITextFields出现,包括as UITableViewCell accessoryViews.

我想设计一种有效的策略,用于在用户触摸UITextField当前正在编辑的内部时解除键盘.我已经搜索了键盘消除技术但尚未找到解释一般键盘解除策略如何工作的答案.

例如,我喜欢这种方法,其中以下代码被添加到任何ViewController:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"* * * * * * * * *ViewControllerBase touchesBegan");

    [self.view endEditing:YES]; // dismiss the keyboard

    [super touchesBegan:touches withEvent:event];
}
Run Code Online (Sandbox Code Playgroud)

......但是这种技术并不涉及例如UITableView在显示器内发生触摸的情况.因此,我需要添加一些代码来endEditingUITableView触摸等时调用等等.这意味着我的应用程序将被大量代码用于在UIElements触摸其他各种键盘时解除键盘.

我想我可以尝试识别触摸需要截取的所有不同的地方,键盘被解雇,但在我看来,在处理iOS键盘关闭事件的某处可能有更好的设计模式.

任何人都可以在这个问题上分享他们的经验,并推荐一种特定的技术来统一处理整个应用程序中的键盘解雇吗?

非常感谢

rob*_*off 26

您的视图层次结构位于UIWindow.该UIWindow负责转发触摸事件在其正确的观点sendEvent:方法.让我们做一个子类UIWindow来覆盖sendEvent:.

@interface MyWindow : UIWindow
@end
Run Code Online (Sandbox Code Playgroud)

窗口将需要对当前第一响应者的引用(如果有的话).您可能决定也使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

窗口将在初始化时开始观察通知,并在取消分配时停止:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

我们将sendEvent:根据事件覆盖"调整"第一响应者,然后调用super sendEvent:来正常发送事件.

- (void)sendEvent:(UIEvent *)event {
    [self adjustFirstResponderForEvent:event];
    [super sendEvent:event];
}
Run Code Online (Sandbox Code Playgroud)

如果没有第一响应者,我们不需要对第一响应者做任何事情.如果有第一响应者,并且它包含触摸,我们不想强迫它辞职.(请记住,可以同时进行多次触摸!)如果有第一个响应者,并且新触摸出现在可以成为第一个响应者的另一个视图中,系统将自动正确处理,因此我们也想忽略该情况.但是如果有第一响应者,并且它不包含任何触摸,并且在不能成为第一响应者的视图中出现新的触摸,我们希望使第一响应者辞职.

- (void)adjustFirstResponderForEvent:(UIEvent *)event {
    if (currentFirstResponder_
        && ![self eventContainsTouchInFirstResponder:event]
        && [self eventContainsNewTouchInNonresponder:event]) {
        [currentFirstResponder_ resignFirstResponder];
    }
}
Run Code Online (Sandbox Code Playgroud)

报告事件是否在第一响应者中包含触摸很容易:

- (BOOL)eventContainsTouchInFirstResponder:(UIEvent *)event {
    for (UITouch *touch in [event touchesForWindow:self]) {
        if (touch.view == currentFirstResponder_)
            return YES;
    }
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

报告事件是否包含无法成为第一响应者的视图中的新触摸几乎一样容易:

- (BOOL)eventContainsNewTouchInNonresponder:(UIEvent *)event {
    for (UITouch *touch in [event touchesForWindow:self]) {
        if (touch.phase == UITouchPhaseBegan && ![touch.view canBecomeFirstResponder])
            return YES;
    }
    return NO;
}

@end
Run Code Online (Sandbox Code Playgroud)

实现此课程后,您需要更改应用程序而不是使用它UIWindow.

如果你正在创建你的UIWindowin application:didFinishLaunchingWithOptions:,你需要在你#import "MyWindow.h"的顶部AppDelegate.m,然后改变application:didFinishLaunchingWithOptions:创建一个MyWindow而不是一个UIWindow.

如果要UIWindow在笔尖中创建,则需要在窗口中设置窗口的自定义类MyWindow.


Yas*_* T. 23

这是一种更容易,更有效的方法.这适用于视图控制器中的任何UITextField.您甚至可以将它添加到您的基本视图控制器(如果您有一个),它将像魅力一样工作.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [[event allTouches] anyObject];

    if (![[touch view] isKindOfClass:[UITextField class]]) {
        [self.view endEditing:YES];
    }
    [super touchesBegan:touches withEvent:event];
}
Run Code Online (Sandbox Code Playgroud)