#YALField
Custom Field component with validation for creating easier form-like UI from interface builder.
##Example Project
To run the example project, run pod try YALField
.
##Usage
To successfully use YALField
component you should take the following steps per each form you have:
-
Make subclass of
YALBaseForm
which lists fields you will have in your form like@property (nonatomic, weak) IBOutlet YALField *fieldName;
. -
Make a subclass of
YALField
and update it as you want. For example make also a subclass ofconfigurator
and set it to theYALField
incommonInit
-
Go make a view controller, add your
fields
on theview
, add someconstraints
configureproperties
as you want and drop aform
as object onto scene at the same level asExit
andFirst Responder
objects. Also don't forget to addfinishButton
of typeUIButton
somewhere on theview
. -
Add
validators
,formatters
andsupplementaryViews
on the same level in scene asform
. -
Wire
IBOutlets
of everyfield
with it'svalidator
,formatter
andsupplementaryView
. You can wirevalidator
andformatter
to as manyfields
as you want but remember you can not use onesupplementaryView
for more than onefield
. -
Go conform
YALFormFinishResponder
andYALErrorsPresenter
to yourviewController
and implement needed methods. -
Then wire
IBOutlets
ofform
withfields
,errorPresenter
,finishResponder
andfinishButton
. -
Done!
##Requirements iOS 8 or higher.
##Installation
####CocoaPods
pod 'YALField', '~> 1.0.1'
####Manual Installation
Alternatively, you can directly add all the source files from YALField to your project.
- Download the latest code version or add the repository as a git submodule to your git-tracked project.
- Open your project in Xcode, then drag and drop all folder directories in Pod/ onto your project (use the "Product Navigator view"). Make sure to select Copy items when asked if you extracted the code archive outside of your project.
- Make concrete subclasses of YALField and YALBaseForm or use them directly from interface builder and import wherever you need it with
#import "YALField.h"
and#import "YALBaseForm.h"
or import your concrete subclasses.
##Introduction
The main two classes you need to know about is YALField
and YALBaseForm
.
YALField
is a the main ui component represents a single field of form. It conforms to protocols YALInput
and YALConstraintBasedUIComponent
publicly and UITextFieldDelegate
and YALFieldInput
privately.
YALBaseForm
is the model made for representing the form, and containing and managing fields of concrete form. YALBaseForm
should be subclassed to have concrete models of forms like YALLoginForm
or YALRegistrationForm
for example which carry concrete form fields. YALBaseForm
conforms to YALForm
protocol.
####Scheme
YALField
is designed to consist of three parts (from left to right):
titleView
of classUILabel
(cyan)textField
of classUITextField
(yellow)supplementaryView
which must be a subclass ofYALBaseSupplementaryView
(magenta)
####Guts
YALField
has a number of properties which define how it looks:
IBOutlet id<YALInputConfigurator> configurator
IBOutlet UIView *supplementaryView
and how it works:
IBOutlet id<YALResponder> responder
IBOutlet id<YALValidator> validator
IBOutlet id<YALFormatter> formatter
####Look
######configurator
configurator
define how YALField
looks. You can subclass YALFieldConfigurator
and set it's properties in init
and/or override following methods: configureView
, configureView:forState
, resetView
. Or you may use one directly from interface builder and modify it's properties there. One configurator
may be used with multiple YALField's
.
######supplementaryView
supplementaryView
is used as view to be added inside the YALField
and used as supplementaryView
. Any view subclassed from YALBaseSupplementaryView
can be used as supplementaryView
of YALField
.
supplementaryView
unlike configurator
can not be shared between multiple YALField's
. Also supplementaryView
should not have superview
or in other words be someones subview
until or after it's set to supplementaryView
property of YALField
cause in that moment it will be immediately added as subview
to YALField
and if you add it to any other view, well that breaks some things.
YALField
pod provides two basic supplementaryView's
: one is used internally and is YALPasswordSupplementaryView
and another one is YALValidatingSupplementaryView
which is made to present validating functionality in supplementaryView
.
YALValidatingSupplementaryView
conforms to protocol YALStateVaryingSupplementaryView
which means it can receive setFieldState
method and change itself whenever fieldState
of YALField
changes.
In the example project you may see YALStepperSupplementaryView
which wraps UIStepper
and which is not only supplementaryView
of YALField
but also responder
which means it handles touches recieved by YALField
and also this concrete supplementaryView
has field
IBOutlet which must be linked to YALField
that owns it so that supplementaryView
may change the formattedValue
of YALField
.
The second supplementaryView
in example project is YALHalloweenSupplementaryView
which is subclass of YALValidatingSupplementaryView
with a slight change of pictures and pictures tint colors for different YALFieldStates
.
####Work
######formatter
formatter
is used to format YALField
value while being input from keyboard or when set from outside with rawValue
setter, also formatter
converts rawValue
to formattedValue
when formattedValue
getter invoked. formatter
must conform to YALFormatter
protocol. One formatter
may be used with many YALFields
.
We provide only one formatter in pod - YALLengthLimitingFormatter
which can be easily used or taken as example when developing formatter
by yourself. Formatters in Example project considered to much of a copy paste or to simple and unique case to be included in pod.
######validator
validator
should be subclass of YALBaseValidator
. validator
is used to validate the YALField
it conforms to protocol YALValidator
which has only one method - (BOOL)isValid:(id<YALInput>)input error:(out NSError **)error;
.
validator
has a property named errorsProviderClassName
which if needed should be the class name of class which should conform to protocol YALErrorsProvider
and can be subclassed from YALBaseErrorsProvider
. This class should provide NSError
for validator
+ (NSError *)errorForValidator:(id<YALValidator>)validator
, most of this logic is encapsulated in YALBaseValidator
which means that in subclass you can just provide this class name and use - (NSError *)error
to get error for validator
.
In pod we provide few basic validators to base your own on:
YALBaseValidator
isvalidator
to make your own customvalidators
from it.YALGroupValidator
provides functionality to group and order multiplevalidators
in itself to use multiplevalidators
on oneYALField
.YALNonEmptyValidator
takesrawValue
ofYALField
asNSString
and checks if it's not empty.YALRegexValidator
takes Regular Expression inregex
IBInspectable
property and checks ifrawValue
ofYALField
matchesregex
.
######responder
responder
is the object which conforms to protocol YALResponder
and may take care of touches received by YALField
by implementing methods like performAction:
or becomeFirstResponder
.
In example project YALStepperSupplementaryView
is responder
of YALField
to prevent YALField
from user interaction. Another example in example project is birthDateResponder
in YALEditProfileViewController
which presents datePicker
when we touch YALField
birthDate
.
###YALBaseForm
YALBaseForm
is a base class for you to subclass your forms from it as for example done YALRegistrationForm
in Example project:
#import "YALBaseForm.h"
@class YALField;
@interface YALRegistrationForm : YALBaseForm
@property (nonatomic, weak) IBOutlet YALField *name;
@property (nonatomic, weak) IBOutlet YALField *phone;
@property (nonatomic, weak) IBOutlet YALField *email;
@property (nonatomic, weak) IBOutlet YALField *password;
@end
Later this form model should be wired with it's fields:
Also you may notice here three not mentionet previously IBOutlets
of YALBaseForm
:
errorsPresenter
is an object conforming toYALErrorsPresenter
protocol which obviously will present anyNSErrors
recieved from form, in this concrete case it'sYALRegistrationViewController
superclassYALBaseViewController
.finishResponder
is an object that conforms toYALResponder
protocol and expected to respond toperformAction:
selector.performAction:
will be invoked when the next interesting objectfinishButton
catchesUIControlEventTouchUpInside
event. AlsofinishResponder
can conform toYALFormFinishResponder
protocol which means it has a propertyform
of typeYALBaseForm
and it will be set if it not yet have been set when form finished.finishButton
is expected to be instance ofUIButton
. When bothfinishButton
andfinishResponder
setYALBaseForm
adds targetself
tofinishButton
to catch event whenfinisButton
will be touched up inside so that form may invokeperformAction:
onfinishResponder
sendingself
assender
.
So in case with YALRegistrationViewController
which is finishResponder
of YALRegistrationForm
you just catch form finish event if and only if every field in form that has a validator
isValid
like this:
@implementation YALRegistrationViewController
...
- (void)performAction:(YALRegistrationForm *)form {
// collect data from form and perform real request to your API for example
[self pretendToDoSomeNetworkRequestWithWithTitle:@"Registrering" completion:^{
[self performSegueWithIdentifier:@"RegistrationToLogin" sender:self];
}];
}
@end
##Author
Igor Muzyka, [email protected]
##License
The MIT License (MIT)
Copyright © 2015 Yalantis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.