DPTextField 0.0.7

DPTextField 0.0.7

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布最后发布2014年12月

Eric Baker 维护。




  • Eric Baker

DPTextField 是 UITextField 的替代控件,它提供了几个有用的功能,包括:

  • Previous/Next 字段工具栏按钮(在界面构建器中可以连接的出口)
  • 不引人注目并可编辑的自填功能
  • Done 工具栏按钮,以及滑动手势,以在不提交表单的情况下移除键盘
  • 设置字段允许的最大字符数
  • 自动处理 Next 和 Done 返回键类型
  • 正确处理 iPhone 和 iPad 的设备旋转
  • 正确处理 iPad 未插入键盘的情况

预览

Auto fill

动机

自动填充使填写表格变得简单得多,尤其是在移动设备上。iOS 上有几种基于 UITextField 的出色控件,可以实现自动填充功能。然而,在我亲自尝试过的几个中,大多数都假定您的表单布局周围有足够的空间来显示下面的自动填充字符串列表,类似于 Google 搜索的自动完成功能。

我不想设计 UI 以容纳自动填充。相反,我希望重用我已经预期用于输入的空间 - iOS 键盘窗口。

幸运的是,Apple 在 UITextField 实例上提供了 inputView 属性。您可以设置任何视图到这个属性,iOS 将显示它而不是标准键盘。太棒了!

但是...仅仅设置自定义输入视图会导致键盘立即消失,新的输入视图立即显示出来。在这样一个动画流畅的操作系统上显得有些突兀。DPTextField 解决了这个问题。

当自动填充列表显示时,iOS 键盘“看起来”滑出,露出下面的字符串列表。选中一个字符串(或如果取消自动填充)后,iOS 键盘“看起来”又滑回原位。这为用户提供了更顺畅的过渡体验。由于重用了 iOS 键盘空间,DPTextField 可以与任何 UI 布局一起使用,无需考虑为自动填充建议留出额外空间。

安装

源代码

或者,您可以克隆此仓库并将 DPTextField 目录中的 5 个文件添加到您的项目中。

用法

工具栏

DPTextField 使用 UIToolbar 作为其 inputAccessoryView。该工具栏能够根据设备方向变化正确调整大小(仅在iPhone上有效)。工具栏的外观可以按需定制。默认情况下,它将显示为标准风格的 UIToolbar 用于普通键盘,以及对于警报键盘使用黑色半透明样式。任何时候,都可以通过将 inputAccessoryViewHidden 属性设置为 YES 来隐藏工具栏。

DPTextField *field = [[DPTextField alloc] init];
[field.toolbar setBarStyle:UIBarStyleBlackOpaque];
[field setInputAccessoryViewHidden:YES];

上一项 | 下一项和完成工具栏按钮

DPTextField 有用于兄弟字段的 IBOutlets,分别命名为 previousFieldnextField。这些可以通过 Interface Builder 连接到其他的 UIResponder 控件。

当这些输出连接时,DPTextField 将在工具栏中显示上一项和下一项按钮。如果允许,这些按钮会切换视图中的首位响应字段。

只有在输出连接时,上一项和下一项按钮才可用。如果没有连接任何一个输出,这些按钮不会显示在工具栏中。也可以通过设置 previousBarButtonEnablednextBarButtonEnabled 的布尔属性手动启用/禁用按钮。这有助于在验证失败时防止字段重新注册首位响应者。(注意,将这些属性设置为 YES 仅在相应的输出连接到另一个控件时启用按钮。)

工具栏中还默认显示一个完成按钮。当单击时,它只是告诉 DPTextField resignFirstResponder。可以通过将 doneBarButtonItemHidden 属性设置为 YES 来移除它。或者,可以通过 doneBarButtonItemEnabled 属性启用/禁用它。

自动填充

DPTextField 接受一个数据源来提供自动填充字符串。数据源可以在代码中设置,也可以在 Interface Builder 中连接,并且必须实现 DPTextFieldAutoFillDataSource 协议。

当提供数据源时,自动填充工具栏按钮会出现在键盘工具栏中。可以手动隐藏该按钮(如果适用),通过设置 autoFillBarButtonItemHidden 布尔属性。注意,如果将此属性设置为 YES,则不会对 autoFillDataSource 属性为 nil 产生作用。按钮的可选状态也是如此。如果数据源没有提供任何匹配的字符串,则按钮不可用。

DPTextFieldAutoFillDataSource 协议提供了几个方法来自定义自动填充行为,但只需提供一种方法。数据源必须提供自己的算法来确定哪些自动填充字符串适合匹配字段中输入的文本。这是一个实现两遍算法的示例方法。第一遍找到以输入文本开头的匹配项。第二遍找到包含输入文本的匹配项。不需要担心匹配数组中的重复项,DPTextField 会自动过滤任何重复项。但是,它不会对匹配项进行排序,因此最好预先排序。

// Return all appropriate auto-fill strings for the given string.
- (NSArray *)textField:(DPTextField *)textField autoFillStringsForString:(NSString *)string {
    NSArray *autoFillStrings = [self allAvailableAutoFillStrings];    // Read from some serialized source

    NSMutableArray *matches = [NSMutableArray array];

    // Pre-sort the autoFillStrings array.
    NSArray *sortedAutoFillStrings = [autoFillStrings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

    // If the search string is nil or empty, just return all auto-fill strings.
    if (nil != string && [string length] > 0) {
        // Match the given string.

        // First pass, find strings with a matching prefix.
        for (NSString *possibleMatch in sortedAutoFillStrings) {
            NSRange range = [possibleMatch rangeOfString:string options:NSCaseInsensitiveSearch];
            if (0 == range.location) {
                [matches addObject:possibleMatch];
            }
        }

        // Second pass, find strings that contain string.
        for (NSString *possibleMatch in sortedAutoFillStrings) {
            NSRange range = [possibleMatch rangeOfString:string options:NSCaseInsensitiveSearch];
            if (NSNotFound != range.location) {
                [matches addObject:possibleMatch];
            }
        }
    } else {
        // Return all available autoFillStrings.
        [matches addObjectsFromArray:sortedAutoFillStrings];
    }
    return matches;
}

可以在查询自动填充字符串之前要求在字段中输入最小的字符数。在数据源中实现 minimumLengthForAutoFillQueryForTextField: 方法,如下所示

- (NSUInteger)minimumLengthForAutoFillQueryForTextField:(DPTextField *)textField {
    // Require at least 3 characters for autoFill in all fields, except field1.
    if (self.field1 == textField) {
        return 1;
    }
    return 3;
}

让用户能够从自动填充字符串列表中删除项是很受欢迎的。您可以通过提供 textField:canRemoveAutoFillString:atIndexPath:textField:removeAutoFillString:atIndexPath: 方法的实现来启用此功能。用户现在可以使用熟悉的水平滑动手势来删除字符串。以下是一个示例

- (BOOL)textField:(DPTextField *)textField canRemoveAutoFillString:(NSString *)string atIndexPath:(NSIndexPath *)indexPath {
    // So long as the string is not one that should never be removed...
    return YES;
}

- (void)textField:(DPTextField *)textField removeAutoFillString:(NSString *)string atIndexPath:(NSIndexPath *)indexPath {
    // Remove the string from the serialized data source.
    // NOTE: Remove only 1 string! If the string count remaining does not match
    // what the auto fill table view expects, then crashes may occur!

    [[self autoFillStrings] removeObject:string];
}

最后,您可以通过实现方法textField:tableView:cellForAutoFillString:atIndexPath:来为自动填充列表提供自己的自定义表格视图单元格。这允许您在应用使用的特定主题之后样式化单元格。如果您不提供此方法,则单元格将默认使用UITableViewCellStyleDefault样式,以及UITableViewCellSelectionStyleGray选择样式。您的实现可能如下所示:(请注意,非编辑单元格是蓝色。)

- (UITableViewCell *)textField:(DPTextField *)textField tableView:(UITableView *)tableView cellForAutoFillString:(NSString *)string atIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"TextFieldAutoFillCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (nil == cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    [[cell textLabel] setText:string];
    [cell setSelectionStyle:UITableViewCellSelectionStyleGray];

    BOOL editable = [self textField:textField canRemoveAutoFillString:string atIndexPath:indexPath];
    [[cell textLabel] setTextColor:(editable ? [UIColor blackColor] : [UIColor blueColor])];

    return cell;
}

自动更正

对于实现了自动填充的字段,通常禁用自动更正是好主意。这可以在Interface Builder中完成。这可能并不总是必要的。我建议测试您的界面是否启用和禁用纠错,以查看哪种最适合您正在处理的数据类型。

最长字符串长度

您可以指定字段的最大允许字符串长度,如下所示

[field setMaximumLength:4];

返回键

如果您的字段使用UIReturnKeyNext返回键,并且下一个工具栏按钮可用,那么在按下Next返回键时,该字段将使下一个字段成为第一响应者。

如果您的字段使用UIReturnKeyDone返回键,并且完成工具栏按钮可用,那么在按下完成返回键时将关闭键盘。

这些行为可以在自定义代理中覆盖。所有其他特殊处理不同的返回键类型都必须在自定义代理中实现。

关于代理...

为了使DPTextField对其超类对象有一定的控制权,它必须实例化自己的内部代理对象,该对象符合UITextFieldDelegate协议。这不会阻止您分配自己的自定义代理对象,无论是通过代码还是在Interface Builder中,就像您使用标准UITextField一样,使用setDelegate:方法。内部代理将服从您的自定义代理。

然而,有一点需要注意,那就是如果您想从DPTextField控件(您很少需要这样做)获取对自定义代理的引用。如果您只是调用DPTextField的delegate属性,您将收到其内部代理实例。为了检索您的自定义代理,请使用customDelegate属性,如下所示

// assuming 'field' is a DPTextField instance...

id<UITextFieldDelegate> myDelegate = [[NSObject alloc] init];
[field setDelegate:myDelegate];     // Or wire up in IB.

id delegate = [field delegate];     // Returns internal delegate!

id customDelegate = [field customDelegate]; // Returns myDelegate.

贡献

随时进行分支和发送拉取请求。选择不同的键盘过渡效果可能是个不错的选择。

App Store 安全吗?

到目前为止,尚不清楚。我计划很快提交一个包括此控件的App Store应用。如果苹果批准该应用,我将更新本部分的README。在此之前,请自行承担风险。如果您的应用获得批准并使用了这个控件,请通知我!

许可

使用是在MIT许可下提供的。请参阅LICENSE以了解全部详情。

致谢

提及总是很好,但不是必须的。至少,请给我发一封电子邮件,让我知道您是否已经很好地使用了这个控件,或者您是否有任何改进的想法。