Easy-to-use library with all the necessary tools to control the screen with custom fields that support data entry and validation.
- Manage screen with number of input fields with custom UI you need
- Use optional and required input fields
- Field validation with success, error and default status
- Taking value for validation, state for changing field appearance and a callback on value change
- Open ValidationRule Protocol for creating custom Rules
- Keyboard up-down tracking
View should implement ValidatableField
protocol, that requires to override three variables:
value
– validated value
validatableState
– the current state of the field (successful, error, default)
onValueChange
– callback, which will be triggered on the change of the field value
In example validatable view is declared as DemoValidatableView: UIView, ValidatableField
and overrides variables this way:
var value: String { textField.text ?? Constants.textFieldDefauldString }
var validatableState: ValidatableFieldState = .default { didSet { updateValidatableState() } }
var onValueChange: ValueCallback?
When field passes validation, validatableState
will get its state. Depending on this state, the UI should also be changed. We did this with didSet
and the updateValidatableState()
function.
private func updateValidatableState() {
switch validatableState {
case .error(let errorDescription):
errorLabel.text = errorDescription ?? Constants.defaultErrorLabel
textField.layer.borderWidth = Constants.textFieldMaxBorderWidth
textField.layer.borderColor = UIColor.red.cgColor
errorLabel.isHidden = false
default:
textField.layer.borderWidth = Constants.textFieldMinBorderWidth
textField.layer.borderColor = UIColor.gray.cgColor
errorLabel.isHidden = true
}
}
The example will implement the following rules:
- The first field must have a string length greater than 5 characters.
- The second field must have a phone number in the string.
- The third field will have two rules: the string length must be less than 5 characters, and the characters themselves must only be the letters 'a'.
To create a rule, implement Rule
protocol. Rule
requires to write a validation function validate
and a property errorMessage
– describing a reason in case of failure.
The rule for the first field will be defined later using a ready-made class MinLenghtRule
.
The package contains a class RegexRule
that implements protocol Rule
. This class is aimed to work with regular expressions, that are needed to check that the string conforms to the defined expression. The RegEx should be passed as the initialization parameter. Example of the implementation:
class PhoneRule: RegexRule {
static let phoneRegex = "(\\+[0-9]+[\\-\\s]?)?(\\(?[0-9]+\\)?[\\-\\s]?)?([0-9][0-9\\-\\s]+[0-9])*"
convenience init() {
self.init(regex: PhoneRule.phoneRegex)
}
}
The rules for the third field can be defined using two classes MaxLenghtRule
and a class that inherits from RegexRule
. Example regex rule implementation:
final class DemoRule: RegexRule {
private enum Constants {
static let demoRegex = "[a]*"
static let errorMessage = "The string consists not only of the letters 'a'"
}
override var errorMessage: String? { Constants.errorMessage }
convenience init() {
self.init(regex: Constants.demoRegex)
}
}
There are few implementation features.
- All fields should be in UIScrollView to keep an ability to lift up the keyboard and scroll to the desired field.
- Confirmation Button should be outside UIScrollVIew and have a constraint set to the bottom of UIScrollView.
- Controller should have access to the scrollView, the button and the fields in some way.
Override aboveKeyboardView
variable . This variable is responsible for View, which will be raised with keyboard up.
override var aboveKeyboardView: UIView? { scrollView }
Override viewDidLoad()
. Include in this function field registation and views initialization
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
registerFields()
setupInitialData()
setupScrollView()
}
-
Create a validator object
validator = FieldValidator()
. -
Register fields for validating.
private func registerFields() {
register(field: validatableFieldOne, rules: [MinLenghtRule(min: Constants.validatableFieldOneMinLenght)])
register(field: validatableFieldTwo, rules: [PhoneRule()])
register(
field: validatableFieldThree,
rules: [
DemoRule(),
MaxLenghtRule(max: Constants.validatableFieldThreeMaxLenght),
MinLenghtRule(min: Constants.validatableFieldThreeMinLenght)
]
)
}
Each field is accompanied by a set of rules. Leave rules
parameter empty if the field doesn't need to be validated.
- Validate fields.
Validator has a function
validator.validate(completion: completion)
that goes through all the fields and validates them. To start the validation call this function. In our example we want to validate fields after user touches up inside button, so we place validate functions inside IBAction.
In case of using scrollView, set scrollView.contentInset.bottom
to button height + button's vertical insets:
private func setupScrollView() {
scrollView.contentInset.bottom = button.frame.height + Constants.buttonYInsets
}
- Swift 5.0 +
- iOS 12.0 +
FormController doesn't contain any external dependencies.
- Make
pod init
- Add the following to Podfile
pod 'FormController', :git => 'https://github.com/MobileUpLLC/FormController', :tag => '1.0.0'
- Make
pod install
Download and drag files from Source folder into your Xcode project.
Swift Package Manager
dependencies: [
.package(url: "https://github.com/MobileUpLLC/FormController", .upToNextMajor(from: "1.0.0"))
]
FormController is distributed under the MIT License.