Swift 库,提供了一系列有用的文本格式化和验证工具,这些工具在您的项目中可能会很有用。
'SmartTextField' 封装了 UITextField,以便利用这些强大的工具,并使其易于与 SwiftUI 一起使用。 它处理文本格式化、验证、错误消息以及格式化文本时光标的正确位置。
#Preview("SmartTextField") {
@State var text: String = ""
@State var errors: [TextValidationResult] = []
return VStack {
Spacer()
SmartTextField(text: $text,
errors: $errors,
configuration: .init(placeholder: "Email",
textFormatter: .email,
textValidator: .email()))
.fixedSize(horizontal: false, vertical: true)
Spacer()
}
}
'UISmartTextField' 封装了 UITextField,以便利用这些强大的工具,并使其易于与 UIKit 一起使用。 它处理文本格式化、验证、错误消息以及格式化文本时光标的正确位置。 UITextField 事件可以通过 'UISmartTextField.Eventier' 中的 'Smart' 闭包来处理。
let email = UISmartTextField()
let emailConfig: UISmartTextField.Configuration
emailConfig = .init(placeholder: "Email",
textFormatter: [
.email,
.stripLeadingAndTrailingSpaces
],
textValidator: [
.notEmpty(errorText: "Email is required"),
.email(errorText: "Invalid email format")
],
textContentType: .emailAddress,
keyboardType: .emailAddress)
email.configure(with: emailConfig)
let password = UISmartTextField()
let passwordConfig: UISmartTextField.Configuration
passwordConfig = .init(placeholder: "Password",
textFormatter: .stripLeadingAndTrailingSpaces,
textValidator: [
.notEmpty(errorText: "Password is required"),
.includesLowerAndUppercase(errorText: "Password must contain lower and uppercase characters"),
.minLengthLimited(8, errorText: "Password must be at least 8 characters long")
],
textContentType: .emailAddress,
keyboardType: .emailAddress)
password.configure(with: passwordConfig)
您可以在 SmartTextField.Configuration
中声明 UIToolbar,以便在表单中的文本字段之间导航。
@FocusState
可以用于处理字段之间的焦点。@FocusState var focus: AuthTextFieldForm?
AuthTextFieldForm
枚举以在字段之间导航。enum AuthTextFieldForm: Int, TextFieldsFormContract {
case email
case password
}
TextFieldsFormContract
是一个协议,它声明了表单字段之间的导航。protocol TextFieldsFormContract: RawRepresentable, Hashable, CaseIterable where RawValue == Int {
func next() -> Self?
func previous() -> Self?
}
extension TextFieldsFormContract where RawValue == Int {
func next() -> Self? {
return .init(rawValue: rawValue + 1)
}
func previous() -> Self? {
return .init(rawValue: rawValue - 1)
}
}
static func toolbar<FocusType: TextFieldsFormContract>(with type: FocusType, focus: FocusState<FocusType?>.Binding) -> UIToolbar {
let doneButton = BlockBarButtonItem(barButtonSystemItem: .done, actionHandler: { focus.wrappedValue = nil })
let prev = type.previous()
let prevButton = BlockBarButtonItem(image: UIImage(systemName: "chevron.left")!, actionHandler: { focus.wrappedValue = prev })
prevButton.isEnabled = prev != nil
let next = type.next()
let nextButton = BlockBarButtonItem(image: UIImage(systemName: "chevron.right")!, actionHandler: { focus.wrappedValue = next })
nextButton.isEnabled = next != nil
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 40))
toolBar.setItems([prevButton, nextButton, flexibleSpace, doneButton], animated: false)
return toolBar
}
final class BlockBarButtonItem: UIBarButtonItem {
private var actionHandler: (() -> Void)?
convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, actionHandler: (() -> Void)?) {
self.init(barButtonSystemItem: systemItem, target: nil, action: #selector(barButtonItemPressed))
self.target = self
self.actionHandler = actionHandler
}
convenience init(title: String, style: UIBarButtonItem.Style = .plain, actionHandler: (() -> Void)?) {
self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed))
self.target = self
self.actionHandler = actionHandler
}
convenience init(image: UIImage, style: UIBarButtonItem.Style = .plain, actionHandler: (() -> Void)?) {
self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed))
self.target = self
self.actionHandler = actionHandler
}
@objc
private func barButtonItemPressed(sender: UIBarButtonItem) {
actionHandler?()
}
}