Input Mask

Pod Version Badge Awesome Actions Android Telegram license

输入掩码可以限制数据输入,并引导用户输入正确的值。
请查看我们的 wiki,以便快速入门和进一步阅读。

⚙️ 功能特性

💳 示例

🛠️ 安装

Swift Package Manager

dependencies: [
    .Package(url: "https://github.com/RedMadRobot/input-mask-ios", majorVersion: 7)
]

CocoaPods

pod 'InputMask'

手动安装

  1. git clone 这个仓库;
  2. InputMask.xcodeproj 添加到您的项目/工作区;
  3. 转到目标设置,在 Embedded Binaries 部分下添加 InputMask.framework
  4. 对于 ObjC 项目
    • (约 Xcode 8.x)确保 Build Options 启用了 Embedded Content Contains Swift Code
    • 导入桥接头文件。

📢 交流、问题和反馈

在将我们的库集成到您的项目中之前,请仔细查看我们的已知问题部分。

对于您的错误报告和功能请求,请通过 GitHub 提交新的 issue。

如果您有任何问题,请搜索已关闭的 issues 或在 StackOverflow 上使用 input-mask 标签提问。

❗已知问题

UITextFieldTextDidChange 通知和目标操作 editingChanged 事件

已分配 MaskedTextFieldDelegate 对象的 UITextField 不会发出 UITextFieldTextDidChange 通知和 editingChanged 控制事件。发生这种情况的原因是 textField(_:shouldChangeCharactersIn:replacementString:) 方法的实现始终返回 false

如果您确实需要捕获编辑事件,请考虑使用以下解决方法

class NotifyingMaskedTextFieldDelegate: MaskedTextFieldDelegate {
    weak var editingListener: NotifyingMaskedTextFieldDelegateListener?
    
    override func textField(
        _ textField: UITextField,
        shouldChangeCharactersIn range: NSRange,
        replacementString string: String
    ) -> Bool {
        defer {
            self.editingListener?.onEditingChanged(inTextField: textField)
        }
        return super.textField(textField, shouldChangeCharactersIn: range, replacementString: string)
    }
}


protocol NotifyingMaskedTextFieldDelegateListener: class {
    func onEditingChanged(inTextField: UITextField)
}

请尽可能避免手动发送 SDK 事件和通知。

Carthage 与 IBDesignables、IBInspectables、视图及其 outlets

Interface Builder 难以支持以动态框架形式导入的模块。例如,注释为 IBDesignable 的自定义视图,其中包含 IBInspectable 和 IBOutlet 字段,无法从拖放的 *.framework 中正确识别。

如果您正在使用我们的库作为 Carthage 构建的动态框架,请注意您将无法轻松地从项目的故事板中连接您的 MaskedTextFieldDelegate 对象及其侦听器。相应的讨论中描述了一些解决方法。

此外,请考虑向 Apple 提交雷达,例如这个

剪切操作不会将文本放入剪贴板

当您剪切文本时,字符会被删除,但您无法将其粘贴到其他地方,因为它们实际上不在您的剪贴板中。

iOS 将 UIMenuController 的剪切操作硬连接到 UITextFieldDelegatetextField(_:shouldChangeCharactersIn:replacementString:) 返回值。这意味着“剪切”行为实际上取决于编辑文本的能力。

坏消息是,我们的库在 textField(_:shouldChangeCharactersIn:replacementString:) 中返回 false,并且严重依赖于这个 false。这将需要我们重写大量的逻辑才能改变这种设计,并且无法保证我们能够做到。

从本质上讲,在 UITextFieldDelegate 端没有明确的方法来区分“剪切选择”和“删除选择”操作。但是,您可以考虑使用一种解决方法,这将需要您子类化 UITextField 并覆盖其 cut(sender:) 方法,如下所示

class UITextFieldMonkeyPatch: UITextField {
    override func cut(_ sender: Any?) {
        copy(sender)
        super.cut(sender)
    }
}

从我们的库的角度来看,这看起来像是一种高度侵入性的解决方案。因此,从长远来看,我们将研究一种“昂贵”的方法来使该行为与 iOS SDK 逻辑匹配。然而,这里的“长远”可能意味着几个月。

粘贴后光标位置不正确

在从剪贴板粘贴新文本后不久,每个 UITextInput 都会从系统收到其 selectedTextRange 属性的新值。这个新的范围与格式化的文本和计算的光标位置大多数时候不一致,但它会在调用 set caretPosition 之后立即被分配。

为了确保设置了正确的光标位置,如果光标移动设置为非原子性的,则可以异步分配(可能在极短的延迟后);请参阅 MaskedTextFieldDelegate.atomicCursorMovement 属性。

MaskedTextInputListener

如果您想知道为什么我们有两个单独的 UITextFieldDelegateUITextViewDelegate 实现,答案很简单:在 **iOS 11** 之前,UITextFieldUITextView 在一些关键情况下具有不同的行为,这使得难以实现通用逻辑。

两者都存在与 UITextInput.beginningOfDocument 属性相关的相同错误,这使得无法使用 UITextFieldUITextView 共有的通用 UITextInput 协议。

自 **iOS 11** 以来,大多数事情都得到了修复(除了 UITextView 边缘情况)。如果您的项目不打算支持低于 11 的任何版本,请考虑使用现代的 MaskedTextInputListener

🙏 特别感谢

这些人太棒了

♻️ 许可证

该库以 MIT LICENSE 许可证分发。