In a previous post we introduce TLFormView , a truly universal generic form for iOS that provides tools for handling all types of devices, orientations and UI/UX experiences out of the box.

Here we present the second part of that post with some of the nicer features that we didn’t include in the first one. Please feel free to check the project home page at GitHub or try the example project through cocoa pods with pod try TLFormView .

TLFormModel. Keep It Stupid Simple [*]

In our previous post were working on an example form for a simple user profile with three fields: photo, user name and age.

But, where is the piece that makes TLFormView absurdly awesome? Right, here: It comes with one more class TLFormModel that allows simplifying the setup process to the absolute minimum.

TLFormModel has a default implementation for the form delegate and data source we just saw in the previous post. This component reads information from the class’ taxonomy so you only need to declare a class that extend TLFormModel and add the properties you want appearing as form fields. Properties must be defined as instances of one of our special types in order to be used as fields in the form.

August 2018: Please note that this post was written for an older version of Swift. Changes in the code might be necessary to adapt it to the latest versions and best practices.

Let’s rewrite the previous example using TLFormModel . Here it is:

@interface UserModel : TLFormModel @property ( nonatomic , strong ) TLFormImage * photo; @property ( nonatomic , strong ) TLFormText * user_name; @property ( nonatomic , strong ) TLFormNumber * age; @end @implementation UserModel @end @implementation ViewController { UserModel * formModel; } - ( void ) viewDidLoad { [super viewDidLoad]; formModel = [[UserModel alloc] init] formModel.photo = TLFormImageValue(userModel.photoUrl); formModel.user_name = TLFormTextValue( @"John Doe" ); formModel.age = TLFormNumberValue( @42 ); TLFormView * formView = [[TLFormView alloc] initWithFrame:self.view.frame]; [formView setFormModel:formModel]; [self.view addSubView:formView]; ... } //Time to read the values out of the form. - ( void ) saveAction { NSDictionary values = @{ @"photo" : formModel.photo, @"user_name" : formModel.user_name, @"age" : formModel.age } ... }

The results should be the same form we saw in the previous post for iPhone.

The default implementation places all fields in a vertical layout, in the same order than the properties were declared. The field titles will by equal to the property name replacing “_” with spaces and capitalising all words.

If we want to adapt the form’s layout in the same way we did before for iPad we just need to override the default implementation of the data source method constraintsFormatForFieldsInForm: in our UserModel class like this:

@implementation UserModel - (NSArray * ) constraintsFormatForFieldsInForm: (TLFormView * )form { //For iPhone we want the default implementation provided so just return the 'super' version if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) return [super constraintsFormatForFieldsInForm:form]; //For anything else use our custom layout else { return @[ //Place the photo on the top left @"V:|-[photo(==230)]" , @"H:|-[photo(==420)]" , //Place the user name at the top and next to the photo @"V:|-[user_name(>=44)]" , @"H:|-[photo]-[user_name]-|" , //Place the age to the right of the photo and below the user name, keeping the width equal to the user name field. @"V:[user_name]-[age(==user_name)]-|" , @"H:|-[photo]-[age]-|" ] ; } } @end

And More

There are two more useful features that we didn’t mentioned yet. Both are part of the TLFormField.

Let’s imagine that we want our form to show the “User Name” field only if the “Age” is greater than 12 years. And we want to explain this strange behaviour with a help button in the “Age” field. To do so we are going to extend our previous UserModel implementation to override another method of the TLFormDataSource protocol. Here is how:

@implementation UserModel - (TLFormField * ) formView: (TLFormView * )form fieldForName: (NSString * )fieldName { //Get the field using the super implementation TLFormField * field = [super formView:form fieldForName:fieldName]; //For find what field we are currently processing we check the 'fieldName' parameter to see if it is equal to the property name we use in the declaration //First we are going to set the conditional visibility of the 'User Name' field. To do so we set a NSPredicate to the 'visibilityPredicate' field property that check the value of the 'age' field and evaluate to YES when it is greater than 12. if ([fieldName isEqualToString: @"user_name" ]) field.visibilityPredicate = [NSPredicate predicateWithFormat: @"$age.value > 12" ]; //Next we need to set the 'helpText' property to an NSString with the text to show as help. When this property has a non-empty value a question mark icon will be shown next to the field title. When the icon is tapped this text will appear in a popup. if ([fieldName isEqualToString: @"age" ]) field.helpText = @"This is the age of the user. Users with less than 12 years are not allowed to enter his name." ; return field; }

The NSPredicate in the property visibilityPredicate receives all the fields in the given form as variables so you can check any field value with the syntax: $[field name].value. More about how it works here.

This is the final result. Here is how the help icon works and how the help is displayed:

Testing

And this is how the visibility predicate works on the edit and read only modes:

Conclusion

TLFormView is a great tool to handle any kind of entity edition and display. It scales very well from simple objects with some fields to big and complex ones, allowing developers to choose how to handle user interaction and look-and-feel. It takes care of the boilerplate behind managing an adaptative solution that takes advantage of all the available capabilities in each device.

Plans for this component are just starting. If you want to learn more check the TLFormView repository on GitHub. If you want to contribute there is a small TODO list of things that I think are needed first. The project has pod support so you can always pod try it with:

pod try TLFormView

The examples presented here are really simple cases, and they do not show all the possibilities that this tool provides. We just reviewed two types of fields out of five! We didn’t talk about special event handling (choosing images, working with the field types TLFormFieldList , etc). So please check the project repo or get the sample project to have a much more complete overview.

It will be really nice to have you involved with this project, or any of the other open source projects at the Tryolabs GitHub, so just come in and have fun!