# Formalist [](https://github.com/Carthage/Carthage) [](https://img.shields.io/cocoapods/v/Formalist.svg) ## Swift framework for building forms on iOS **Formalist** is a Swift framework for building forms on iOS using a simple, declarative, and readable syntax. ## Table of Contents - [Example](#example) - [Installation](#installation) - [CocoaPods](#cocoapods) - [Carthage](#carthage) - [Documentation](#documentation) - [`FormValue`](#formvalue) - [`FormElement`](#formelement) - [Responder Chain](#responder-chain) - [`ValidationRule`](#validationrule) - [`Validatable`](#validatable) - [`GroupElement`](#groupelement) - [Examples](#examples) - [Using constant height form elements](#using-constant-height-form-elements) - [Using intrinsically sized form elements with padding](#using-intrinsically-sized-form-elements-with-padding) - [Using a custom separator view](#using-a-custom-separator-view) - [Using a custom valiation error view](#using-a-custom-valiation-error-view) - [`BooleanElement`](#booleanelement) - [`SegmentElement`](#segmentelement) - [`SpacerElement`](#spacerelement) - [`SegueElement`](#segueelement) - [`StaticTextElement`](#statictextelement) - [`EditableTextElement`](#editabletextelement) - [Text Field](#text-field) - [Text View](#text-view) - [Float Label](#float-label) - [`ViewElement`](#viewelement) - [`FormViewController`](#formviewcontroller) - [Testing](#testing) - [License](#license) - [Acknowledgements](#acknowledgements) ## Example
`GroupElement` is the main non-leaf node type in a form element hierarchy, and implements a number of important behaviours related to the rendering, layout, validation, and interaction amongst a group of form elements.
It implements two styles: `Plain`, which renders the group with no background color and no separators, and `Grouped`, which renders the group with a given background color and separators by default.
The `GroupElement.Configuration` object passed into the initializer can be used to tweak most aspects of the group appearance and behaviour, including the style, layout, separator views, and validation error views.
#### Examples
##### Using constant height form elements
```swift
var configuration = GroupElement.Configuration()
configuration.layout.mode = .ConstantHeight(44)
let groupElement = GroupElement(configuration: configuration, elements: [...])
```
##### Using intrinsically sized form elements with padding
```swift
var configuration = GroupElement.Configuration()
configuration.style = .Grouped(backgroundColor: .whiteColor())
configuration.layout.edgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
let groupElement = GroupElement(configuration: configuration, elements: [...])
```
##### Using a custom separator view
```swift
var configuration = GroupElement.Configuration()
configuration.separatorViewFactory = { (style, isBorder) in
let separatorView = SeparatorView(axis: .Horizontal)
separatorView.separatorInset = isBorder ? 0 : 20.0
separatorView.separatorColor = .redColor()
separatorView.separatorThickness = 2.0
return separatorView
}
let groupElement = GroupElement(configuration: configuration, elements: [...])
```
##### Using a custom valiation error view
```swift
var configuration = GroupElement.Configuration()
configuration.validationErrorViewFactory = { message in
let label = UILabel(frame: CGRectZero)
label.textColor = .redColor()
label.textAlignment = .Center
label.text = message
return label
}
let groupElement = GroupElement(configuration: configuration, elements: [...])
```
### `BooleanElement`
`BooleanElement` displays a `UILabel` and a `UISwitch` that is bound to a `Bool` value.
```swift
toggle(title: "Boolean Element", value: self.booleanValue)
```
### `SegmentElement`
`SegmentElement` displays a `UILabel` and a `UISegmentedControl` that is bound to a `Segment
`StaticTextElement` displays static, non-editable text using a `UILabel`.
```swift
staticText("Welcome to the Forms Catalog app. This text is an example of a StaticTextElement. Other kinds of elements are showcased below.") {
$0.textAlignment = .Center
$0.font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)
}
```
### `EditableTextElement`
`EditableTextElement` is an element that is bound to a `String` value and is capable of rendering several different controls for editable text. While these elements can be created by instantiating `EditableTextElement` directly, it is much simpler to use the convenience functions as shown in the code snippets below.
This element supports a number of different configuration options specified using the `TextEditorConfiguration` struct, including custom Return key behaviours (in addition to the standard tabbing behaviour between fields), as well as validation of the `String` value.
#### Text Field
Displays a `UITextView` for multiple lines of editable text. The text view is actually an instance of `PlaceholderTextView`, which is a `UITextView` subclass that adds support for a placeholder string using the same API as `UITextField`.
```swift
textView(value: self.textViewValue) {
$0.placeholder = "Text View Element"
}
```
#### Float Label
Implements a native iOS version of the [float label pattern](http://bradfrost.com/blog/post/float-label-pattern/). This concept is excellent for maintaining the context of the field label regardless of whether text has been entered in the field or not, unlike a traditional placeholder.
Float Label comes in two flavors: single line (instantiated using the `singleLineFloatLabel` function) and multi-line (instantiated using the `multiLineFloatLabel` function). The single line variant uses a `UITextField` as its underlying editor view, and the multi-line variant uses a `PlaceholderTextView` (`UITextView subclass`). In both cases, the underlying editor view can be accessed via the `FloatLabel.textEntryView` property inside the optional view configurator block.
```swift
singleLineFloatLabel(name: "Float Label Element", value: self.floatLabelValue)
```
### `ViewElement`
`ViewElement` provides an easy way to wrap an existing view to create a one-off custom form element without any subclassing. It is used to implement the activity indicator element shown in the example application. It has to be initialized with a `FormValue` instance, which is then passed into the block that creates the custom view. However, if the view is not bound to a value (like in the activity indicator), you may simply pass a dummy value and ignore it inside the block.
```swift
customView(value: FormValue("")) { _ in
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
activityIndicator.startAnimating()
return activityIndicator
}
```
### `FormViewController`
`FormViewController` is a `UIViewController` subclass that can be used standalone or embedded inside a parent view controller using [view controller containment](https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html) to display form content in your app.
```swift
let formViewController = FormViewController(elements: [
singleLineFloatLabel(name: "Name", value: self.nameValue),
singleLineFloatLabel(name: "Email", value: self.emailValue, validationRules: [.email]),
singleLineFloatLabel(name: "Password", value: self.passwordValue) {
$0.textEntryView.secureTextEntry = true
}
])
addChildViewController(formViewController)
view.addSubview(formViewController.view)
formViewController.view.activateSuperviewHuggingConstraints()
formViewController.didMoveToParentViewController(self)
```
## Testing
The framework is tested using a combination of snapshot tests via [FBSnapshotTestCase](https://github.com/facebook/ios-snapshot-test-case) and automated UI testing in Xcode.
## License
This project is licensed under the MIT license. See `LICENSE.md` for more details.
## Acknowledgements
Formalist uses [StackViewController](https://github.com/seedco/StackViewController) and [FBSnapshotTestCase](https://github.com/facebook/ios-snapshot-test-case).