XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C.

Last update: May 13, 2022

XLForm

By XMARTLABS.

Build Status CocoaPods compatible

If you are working in Swift then you should have a look at Eureka, a complete re-design of XLForm in Swift and with more features.

We are not implementing any new features for XLForm anymore. However, if a critical issue arises we will fix it.

Purpose

XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. The goal of the library is to get the same power of hand-made forms but spending 1/10 of the time.

XLForm provides a very powerful DSL (Domain Specific Language) used to create a form. It keeps track of this specification on runtime, updating the UI on the fly.

Let's see the iOS Calendar Event Form created using XLForm

Screenshot of native Calendar Event Example

What XLForm does

  • Loads a form based on a declarative form definition.
  • Keeps track of definition changes on runtime to update the form interface accordingly. Further information on Dynamic Forms section of this readme.
  • Supports multivalued sections allowing us to create, delete or reorder rows. For further details see Multivalued Sections section bellow.
  • Supports custom rows definition.
  • Supports custom selectors. For further details of how to define your own selectors check Custom selectors section out.
  • Provides several inline selectors such as date picker and picker inline selectors and brings a way to create custom inline selectors.
  • Form data validation based on form definition.
  • Ability to easily navigate among rows, fully customizable.
  • Ability to show inputAccessoryView if needed. By default a navigation input accessory view is shown.
  • Read only mode for a particular row or the entire form.
  • Rows can be hidden or shown depending on other rows values. This can be done declaratively using NSPredicates. (see Make a row or section invisible depending on other rows values)

How to create a form

Create an instance of XLFormViewController

Swift
class CalendarEventFormViewController : XLFormViewController {

  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.initializeForm()
  }


  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    self.initializeForm()
  }

  func initializeForm() {
    // Implementation details covered in the next section.
  }

}
Objective-C
#import "XLFormViewController.h"

@interface CalendarEventFormViewController: XLFormViewController

@end
@interface ExamplesFormViewController ()

@end

@implementation ExamplesFormViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self){
        [self initializeForm];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self){
        [self initializeForm];
    }
    return self;
}

- (void)initializeForm {
  // Implementation details covered in the next section.
}

@end
Implementing the initializeForm method

To create a form we should declare it through a XLFormDescriptor instance and assign it to a XLFormViewController instance. As we said XLForm works based on a DSL that hides complex and boilerplate stuff without losing the power and flexibility of hand-made forms.

To define a form we use 3 classes:

  • XLFormDescriptor
  • XLFormSectionDescriptor
  • XLFormRowDescriptor

A form definition is a XLFormDescriptor instance that contains one or more sections (XLFormSectionDescriptor instances) and each section contains several rows (XLFormRowDescriptor instance). As you may have noticed the DSL structure is analog to the structure of a UITableView (Table -->> Sections -- >> Rows). The resulting table-view form's structure (sections and rows order) mirrors the definition's structure.

Let's see an example implementation of initializeForm to define the iOS Calendar Event Form
- (void)initializeForm {
  XLFormDescriptor * form;
  XLFormSectionDescriptor * section;
  XLFormRowDescriptor * row;

  form = [XLFormDescriptor formDescriptorWithTitle:@"Add Event"];

  // First section
  section = [XLFormSectionDescriptor formSection];
  [form addFormSection:section];

  // Title
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
  [row.cellConfigAtConfigure setObject:@"Title" forKey:@"textField.placeholder"];
  [section addFormRow:row];

  // Location
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"location" rowType:XLFormRowDescriptorTypeText];
  [row.cellConfigAtConfigure setObject:@"Location" forKey:@"textField.placeholder"];
  [section addFormRow:row];

  // Second Section
  section = [XLFormSectionDescriptor formSection];
  [form addFormSection:section];

  // All-day
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"all-day" rowType:XLFormRowDescriptorTypeBooleanSwitch title:@"All-day"];
  [section addFormRow:row];

  // Starts
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"starts" rowType:XLFormRowDescriptorTypeDateTimeInline title:@"Starts"];
  row.value = [NSDate dateWithTimeIntervalSinceNow:60*60*24];
  [section addFormRow:row];

  self.form = form;
}

XLForm will load the table-view form from the previously explained definition. The most interesting part is that it will update the table-view form based on the form definition modifications. That means that we are able to make changes on the table-view form adding or removing section definitions or row definitions to the form definition on runtime and you will never need to care again about NSIndexPath, UITableViewDelegate, UITableViewDataSource or other complexities.

To see more complex form definitions take a look at the example application in the Examples folder of this repository. You can also run the examples on your own device if you wish. XLForm has no dependencies over other pods, anyway the examples project makes use of some cocoapods to show advanced XLForm features.

Using XLForm with Storyboards

  • Perform the steps from How to create a form
  • In Interface Builder (IB), drag-and-drop a UIViewController onto the Storyboard
  • Associate your custom form class to the UIViewController using the Identity Inspector

How to run XLForm examples

  1. Clone the repository [email protected]:xmartlabs/XLForm.git. Optionally you can fork the repository and clone it from your own github account, this approach would be better in case you want to contribute.
  2. Move to either the Objective-c or Swift example folder.
  3. Install example project cocoapod dependencies. From inside Objective-c or Swift example folder run pod install.
  4. Open XLForm or SwiftExample workspace using XCode and run the project. Enjoy!

Rows

Input Rows

Screenshot of Input Examples

Input rows allows the user to enter text values. Basically they use UITextField or UITextView controls. The main differences among the input row types is the keyboardType, autocorrectionType and autocapitalizationType configuration.

static NSString *const XLFormRowDescriptorTypeText = @"text";

Will be represented by a UITextField with UITextAutocorrectionTypeDefault, UITextAutocapitalizationTypeSentences and UIKeyboardTypeDefault.

static NSString *const XLFormRowDescriptorTypeName = @"name";

Will be represented by a UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeWords and UIKeyboardTypeDefault.

static NSString *const XLFormRowDescriptorTypeURL = @"url";

Will be represented by a UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNone and UIKeyboardTypeURL.

static NSString *const XLFormRowDescriptorTypeEmail = @"email";

Will be represented by a UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNone and UIKeyboardTypeEmailAddress.

static NSString *const XLFormRowDescriptorTypePassword = @"password";

Will be represented by a UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNone and UIKeyboardTypeASCIICapable. This row type also set the secureTextEntry to YES in order to hide what the user types.

static NSString *const XLFormRowDescriptorTypeNumber = @"number";

Will be represented by a UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNone and UIKeyboardTypeNumbersAndPunctuation.

static NSString *const XLFormRowDescriptorTypePhone = @"phone";

Will be represented by a UITextField with UIKeyboardTypePhonePad.

static NSString *const XLFormRowDescriptorTypeTwitter = @"twitter";

Will be represented by a UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNone and UIKeyboardTypeTwitter.

static NSString *const XLFormRowDescriptorTypeAccount = @"account";

Will be represented by a UITextField with UITextAutocorrectionTypeNo, UITextAutocapitalizationTypeNone and UIKeyboardTypeDefault.

static NSString *const XLFormRowDescriptorTypeInteger = @"integer";

Will be represented by a UITextField with UIKeyboardTypeNumberPad.

static NSString *const XLFormRowDescriptorTypeDecimal = @"decimal";

Will be represented by a UITextField with UIKeyboardTypeDecimalPad.

static NSString *const XLFormRowDescriptorTypeTextView = @"textView";

Will be represented by a UITextView with UITextAutocorrectionTypeDefault, UITextAutocapitalizationTypeSentences and UIKeyboardTypeDefault.

Selector Rows

Selector rows allow us to select a value or values from a list. XLForm supports 8 types of selectors out of the box:

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeSelectorPush = @"selectorPush";
static NSString *const XLFormRowDescriptorTypeSelectorActionSheet = @"selectorActionSheet";
static NSString *const XLFormRowDescriptorTypeSelectorAlertView = @"selectorAlertView";
static NSString *const XLFormRowDescriptorTypeSelectorLeftRight = @"selectorLeftRight";
static NSString *const XLFormRowDescriptorTypeSelectorPickerView = @"selectorPickerView";
static NSString *const XLFormRowDescriptorTypeSelectorPickerViewInline = @"selectorPickerViewInline";
static NSString *const XLFormRowDescriptorTypeSelectorSegmentedControl = @"selectorSegmentedControl";
static NSString *const XLFormRowDescriptorTypeMultipleSelector = @"multipleSelector";

Normally we will have a collection of object to select (these objects should have a string to display them and a value in order to serialize them), XLForm has to be able to display these objects.

XLForm follows the following rules to display an object:

  1. If the value of the XLFormRowDescriptor object is nil, XLForm uses the noValueDisplayText row property as display text.
  2. If the XLFormRowDescriptor instance has a valueTransformer property value. XLForm uses the NSValueTransformer to convert the selected object to a NSString.
  3. If the object is a NSString or NSNumber it uses the object description property.
  4. If the object conforms to protocol XLFormOptionObject, XLForm gets the display value from formDisplayText method.
  5. Otherwise it return nil. That means you should conforms the protocol :).

You may be interested in change the display text either by setting up noValueDisplayText or valueTransformer property or making the selector options objects to conform to XLFormOptionObject protocol.

This is the protocol declaration:

@protocol XLFormOptionObject <NSObject>

@required
-(NSString *)formDisplayText;
-(id)formValue;

@end

Date & Time Rows

XLForms supports 3 types of dates: Date, DateTime , Time and Countdown Timer and it's able to present the UIDatePicker control in 2 different ways, inline and non-inline.

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeDateInline = @"dateInline";
static NSString *const XLFormRowDescriptorTypeDateTimeInline = @"datetimeInline";
static NSString *const XLFormRowDescriptorTypeTimeInline = @"timeInline";
static NSString *const XLFormRowDescriptorTypeCountDownTimerInline = @"countDownTimerInline";
static NSString *const XLFormRowDescriptorTypeDate = @"date";
static NSString *const XLFormRowDescriptorTypeDateTime = @"datetime";
static NSString *const XLFormRowDescriptorTypeTime = @"time";
static NSString *const XLFormRowDescriptorTypeCountDownTimer = @"countDownTimer";

Here is an example of how to define these row types:

Objective C

XLFormDescriptor * form;
XLFormSectionDescriptor * section;
XLFormRowDescriptor * row;

form = [XLFormDescriptor formDescriptorWithTitle:@"Dates"];

section = [XLFormSectionDescriptor formSectionWithTitle:@"Inline Dates"];
[form addFormSection:section];

// Date
row = [XLFormRowDescriptor formRowDescriptorWithTag:kDateInline rowType:XLFormRowDescriptorTypeDateInline title:@"Date"];
row.value = [NSDate new];
[section addFormRow:row];

// Time
row = [XLFormRowDescriptor formRowDescriptorWithTag:kTimeInline rowType:XLFormRowDescriptorTypeTimeInline title:@"Time"];
row.value = [NSDate new];
[section addFormRow:row];

// DateTime
row = [XLFormRowDescriptor formRowDescriptorWithTag:kDateTimeInline rowType:XLFormRowDescriptorTypeDateTimeInline title:@"Date Time"];
row.value = [NSDate new];
[section addFormRow:row];

// CountDownTimer
row = [XLFormRowDescriptor formRowDescriptorWithTag:kCountDownTimerInline rowType:XLFormRowDescriptorTypeCountDownTimerInline title:@"Countdown Timer"];
row.value = [NSDate new];
[section addFormRow:row];

Swift

static let dateTime = "dateTime"
static let date = "date"
static let time = "time"

var form : XLFormDescriptor
var section : XLFormSectionDescriptor
var row : XLFormRowDescriptor

form = XLFormDescriptor(title: "Dates") as XLFormDescriptor

section = XLFormSectionDescriptor.formSectionWithTitle("Inline Dates") as XLFormSectionDescriptor
form.addFormSection(section)

// Date
row = XLFormRowDescriptor(tag: tag.date, rowType: XLFormRowDescriptorTypeDateInline, title:"Date")
row.value = NSDate()
section.addFormRow(row)

// Time
row = XLFormRowDescriptor(tag: tag.time, rowType: XLFormRowDescriptorTypeTimeInline, title: "Time")
row.value = NSDate()
section.addFormRow(row)

// DateTime
row = XLFormRowDescriptor(tag: tag.dateTime, rowType: XLFormRowDescriptorTypeDateTimeInline, title: "Date Time")
row.value = NSDate()
section.addFormRow(row)

self.form = form;

Boolean Rows

XLForms supports 2 types of boolean controls:

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeBooleanCheck = @"booleanCheck";
static NSString *const XLFormRowDescriptorTypeBooleanSwitch = @"booleanSwitch";

We can also simulate other types of Boolean rows using any of the Selector Row Types introduced in the Selector Rows section.

Other Rows

Stepper

XLForms supports counting using UIStepper control:

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeStepCounter = @"stepCounter";

You can set the stepper paramaters easily:

	row = [XLFormRowDescriptor formRowDescriptorWithTag:kStepCounter rowType:XLFormRowDescriptorTypeStepCounter title:@"Step counter"];
	row.value = @50;
	[row.cellConfigAtConfigure setObject:@YES forKey:@"stepControl.wraps"];
	[row.cellConfigAtConfigure setObject:@10 forKey:@"stepControl.stepValue"];
	[row.cellConfigAtConfigure setObject:@10 forKey:@"stepControl.minimumValue"];
	[row.cellConfigAtConfigure setObject:@100 forKey:@"stepControl.maximumValue"];
Slider

XLForms supports counting using UISlider control:

static NSString *const XLFormRowDescriptorTypeSlider = @"slider";

You can adjust the slider for your own interests very easily:

	row = [XLFormRowDescriptor formRowDescriptorWithTag:kSlider rowType:XLFormRowDescriptorTypeSlider title:@"Slider"];
	row.value = @(30);
	[row.cellConfigAtConfigure setObject:@(100) forKey:@"slider.maximumValue"];
	[row.cellConfigAtConfigure setObject:@(10) forKey:@"slider.minimumValue"];
	[row.cellConfigAtConfigure setObject:@(4) forKey:@"steps"];

Set steps to @(0) to disable the steps functionality.

Info

Sometimes our apps needs to show data that are not editable. XLForm provides us with XLFormRowDescriptorTypeInfo row type to display not editable info. An example of usage would be showing the app version in the settings part of an app.

Button

Apart from data entry rows, not editable rows and selectors, XLForm has a button row XLFormRowDescriptorTypeButton that allows us to do any action when selected. It can be configured using a block (clousure), a selector, a segue identifier, segue class or specifing a view controller to be presented. ViewController specification could be done by setting up the view controller class, the view controller storyboard Id or a nib name. Nib name must match view controller class name.

Multivalued Sections (Insert, Delete, Reorder rows)

Any XLFormSectionDescriptor object can be set up to support row insertion, deletion or reodering. It is possible to enable only one of these modes, a combination or all together. A multivalued section is just a section that support either of these modes.

The most interesting part of multivalued XLFormSectionDescriptor is that it supports all the types of rows that were shown on the Rows section as well as custom rows.

Screenshot of Multivalued Section Example

How to set up a multivalued section

Creating a multivalued section is as simple as use one of the following convenience XLFormSectionDescriptor initializer:

+(id)formSectionWithTitle:(NSString *)title
		   sectionOptions:(XLFormSectionOptions)sectionOptions;
+(id)formSectionWithTitle:(NSString *)title
		   sectionOptions:(XLFormSectionOptions)sectionOptions
		sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode;

sectionOptions is a bitwise enum parameter that should be used to choose the multivalued section type/s (insert, delete, reorder). Available options are XLFormSectionOptionCanInsert, XLFormSectionOptionCanDelete, XLFormSectionOptionCanReorder. XLFormSectionOptionNone is the value used by default.

sectionInsertMode can be used to select how the insertion mode will look like. XLform has 2 different insertion modes out of the box: XLFormSectionInsertModeLastRow and XLFormSectionInsertModeButton. XLFormSectionInsertModeLastRow is the default value.

Let's see how to create a multivalued section

XLFormDescriptor * form;
XLFormSectionDescriptor * section;
XLFormRowDescriptor * row;

NSArray * nameList = @[@"family", @"male", @"female", @"client"];

form = [XLFormDescriptor formDescriptorWithTitle:@"Multivalued examples"];

// Enable Insertion, Deletion, Reordering
section = [XLFormSectionDescriptor formSectionWithTitle:@"MultiValued TextField"
										  sectionOptions:XLFormSectionOptionCanReorder | XLFormSectionOptionCanInsert | XLFormSectionOptionCanDelete];
section.multivaluedTag = @"textFieldRow";
[form addFormSection:section];

for (NSString * tag in nameList) {
	// add a row to the section, each row will represent a name of the name list array.
	row = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];
	[[row cellConfig] setObject:@"Add a new tag" forKey:@"textField.placeholder"];
	row.value = [tag copy];
	[section addFormRow:row];
}
// add an empty row to the section.
row = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];
[[row cellConfig] setObject:@"Add a new tag" forKey:@"textField.placeholder"];
[section addFormRow:row];

Form Values

formValues

You can get all form values invoking -(NSDictionary *)formValues; either XLFormViewController instance or XLFormDescriptor instance.

The returned NSDictionary is created following this rules:

XLForm adds a value for each XLFormRowDescriptor that belongs to a XLFormSectionDescriptor doesn't have a multivaluedTag value set up. The dictionary key is the value of XLFormRowDescriptor tag property.

For each section that has a multivaluedTag value, XLForm adds a dictionary item with a NSArray as a value, each value of the array is the value of each row contained in the section, and the key is the multivaluedTag.

For instance, if we have a section with the multivaluedTag property equal to tags and the following values on the contained rows: 'family', 'male', 'female', 'client', the generated value will be tags: ['family', 'male', 'female', 'client']

httpParameters

In same cases the form value we need may differ from the value of XLFormRowDescriptor instance. This is usually the case of selectors row and when we need to send the form values to some endpoint, the selected value could be a core data object or any other object. In this cases XLForm need to know how to get the value and the description of the selected object.

When using -(NSDictionary *)httpParameters method, XLForm follows the following rules to get XLFormRowDescriptor value:

  1. If the object is a NSString, NSNumber or NSDate, the value is the object itself.
  2. If the object conforms to protocol XLFormOptionObject, XLForm gets the value from formValue method.
  3. Otherwise it return nil.

multivaluedTag works in the same way as in formValues method.

How to create a Custom Row

To create a custom cell you need to create a UITableViewCell extending from XLFormBaseCell. XLFormBaseCell conforms to XLFormDescriptorCell protocol.

You may be interested in implement XLFormDescriptorCell methods to change the cell behaviour.

@protocol XLFormDescriptorCell <NSObject>

@required

@property (nonatomic, weak) XLFormRowDescriptor * rowDescriptor;

// initialise all objects such as Arrays, UIControls etc...
-(void)configure;
// update cell when it about to be presented
-(void)update;

@optional

// height of the cell
+(CGFloat)formDescriptorCellHeightForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor;
// called to check if cell can became first responder
-(BOOL)formDescriptorCellCanBecomeFirstResponder;
// called to ask cell to assign first responder to relevant UIView.
-(BOOL)formDescriptorCellBecomeFirstResponder;
// called when cell is selected
-(void)formDescriptorCellDidSelectedWithFormController:(XLFormViewController *)controller;
// http parameter name used for network request
-(NSString *)formDescriptorHttpParameterName;

// is invoked when cell becomes firstResponder, could be used for change how the cell looks like when it's the forst responder.
-(void)highlight;
// is invoked when cell resign firstResponder
-(void)unhighlight;


@end

Once a custom cell has been created you need to let XLForm know about this cell by adding the row definition to cellClassesForRowDescriptorTypes dictionary.

[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:[MYCustomCellClass class] forKey:kMyAppCustomCellType];

or, in case we have used nib file to define the XLBaseDescriptorCell:

[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:@"nibNameWithoutNibExtension" forKey:kMyAppCustomCellType];

Doing that, XLForm will instantiate the proper cell class when kMyAppCustomCellType row type is used.

Custom Selectors - Selector Row with a custom selector view controller

Almost always the basic selector which allows the user to select one or multiple items from a pushed view controller is enough for our needs, but sometimes we need more flexibility to bring a better user experience to the user or do something not supported by default.

Let's say your app user needs to select a map coordinate or it needs to select a value fetched from a server endpoint. How do we do that easily?

Screenshot of Map Custom Selector

Define the previous selector row is as simple as ...

row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorMap rowType:XLFormRowDescriptorTypeSelectorPush title:@"Coordinate"];
// set up the selector controller class
row.action.viewControllerClass = [MapViewController class];
// or
//row.action.viewControllerStoryboardId = @"MapViewControllerStoryboardId";
// or
//row.action.viewControllerNibName = @"MapViewControllerNibName";

// Set up a NSValueTransformer to convert CLLocation to NSString, it's used to show the select value description (text).
row.valueTransformer = [CLLocationValueTrasformer class];
// Set up the default value
row.value = [[CLLocation alloc] initWithLatitude:-33 longitude:-56];

action.viewControllerClass controller class should conform to XLFormRowDescriptorViewController protocol.

In the example above, MapViewController conforms to XLFormRowDescriptorViewController.

@protocol XLFormRowDescriptorViewController <NSObject>

@required
@property (nonatomic) XLFormRowDescriptor * rowDescriptor;

@end

XLForm sets up rowDescriptor property using the XLFormRowDescriptor instance that belongs to the selector row.

The developer is responsible for update its views with the rowDescriptor value as well as set the selected value to rowDescriptor from within the custom selector view controller.

Note: the properties viewControllerClass, viewControllerNibName or viewControllerStoryboardId are mutually exclusive and are used by XLFormButtonCell and XLFormSelectorCell. If you create a custom cell then you are responsible for using them.

Another example

Screenshot of Dynamic Custom Selector

row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorUser rowType:XLFormRowDescriptorTypeSelectorPush title:@"User"];
row.action.viewControllerClass = [UsersTableViewController class];

You can find the details of these examples within the example repository folder, Examples/Objective-C/Examples/Selectors/CustomSelectors/ and Examples/Objective-C/Examples/Selectors/DynamicSelector.

Dynamic Forms - How to change the form dynamically at runtime

Any change made on the XLFormDescriptor will be reflected on the XLFormViewController tableView. That means that when a section or a row is added or removed XLForm will animate the section or row accordingly.

We shouldn't have to deal with NSIndexPaths or add, remove UITableViewCell anymore. NSIndexPath of a specific TableViewCell changes along the time and this makes very hard to keep track of the NSIndexPath of each UITableViewCell.

Each XLForm XLFormRowDescriptor row has a tag property that is set up in its constructor. XLFormDescriptor has, among other helpers, an specific one to get a XLFormRowDescriptor from a tag. It's much easier to manage XLFormRowDescriptors using tags, the tag should be unique and it doesn't change on tableview additions modifications or deletions.

It's important to keep in mind that all the UITableView form modifications have to be made using the descriptors and not making modifications directly on the UITableView.

Usually you may want to change the form when some value change or some row or section is added or removed. For this you can set the disabled and hidden properties of the rows or sections. For more details see Make a row or section invisible depending on other rows values.

In order to stay in sync with the form descriptor modifications your XLFormViewController subclass should override the XLFormDescriptorDelegate methods of 'XLFormViewController'.

Note: It is important to always call the [super ...] method when overriding this delegate's methods.

@protocol XLFormDescriptorDelegate <NSObject>

@required

-(void)formSectionHasBeenRemoved:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;
-(void)formSectionHasBeenAdded:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;
-(void)formRowHasBeenAdded:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;
-(void)formRowHasBeenRemoved:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;
-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue;
-(void)formRowDescriptorPredicateHasChanged:(XLFormRowDescriptor *)formRow
                                   oldValue:(id)oldValue
                                   newValue:(id)newValue
                              predicateType:(XLPredicateType)predicateType;

@end

For instance if we want to show or hide a row depending on the value of another row:

-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue
{
	// super implmentation MUST be called
	[super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];
    if ([rowDescriptor.tag isEqualToString:@"alert"]){
        if ([[rowDescriptor.value valueData] isEqualToNumber:@(0)] == NO && [[oldValue valueData] isEqualToNumber:@(0)]){
            XLFormRowDescriptor * newRow = [rowDescriptor copy];
            [newRow setTag:@"secondAlert"];
            newRow.title = @"Second Alert";
            [self.form addFormRow:newRow afterRow:rowDescriptor];
        }
        else if ([[oldValue valueData] isEqualToNumber:@(0)] == NO && [[newValue valueData] isEqualToNumber:@(0)]){
            [self.form removeFormRowWithTag:@"secondAlert"];
        }
    }

Make a row or section invisible depending on other rows values

Summary

XLForm allows you to define dependencies between rows so that if the value of one row is changed, the behaviour of another one changes automatically. For example, you might have a form where you question the user if he/she has pets. If the answer is 'yes' you might want to ask how their names are. So you can make a row invisible and visible again based on the values of other rows. The same happens with sections. Take a look at the following example:

Screenshot of hiding rows

Of course, you could also do this manually by observing the value of some rows and deleting and adding rows accordingly, but that would be a lot of work which is already done.

How it works

To make the appearance and disappearance of rows and sections automatic, there is a property in each descriptor:

@property id hidden;

This id object will normally be a NSPredicate or a NSNumber containing a BOOL. It can be set using any of them or eventually a NSString from which a NSPredicate will be created. In order for this to work the string has to be syntactically correct.

For example, you could set the following string to a row (second) to make it disappear when a previous row (first) contains the value "hide".

second.hidden = [NSString stringWithFormat:@"$%@ contains[c] 'hide'", first];

This will insert the tag of the first after the '$', you can do that manually as well, of course. When the predicate is evaluated every tag variable gets substituted by the corresponding row descriptor.

When the argument is a NSString, a '.value' will be appended to every tag unless the tag is followed by '.isHidden' or '.isDisabled'. This means that a row (or section) might depend on the value or the hidden or disabled properties of another row. When the property is set with a NSPredicate directly, its formatString will not be altered (so you have to append a '.value' after each variable if you want to refer to its value). Setting a NSString is the simplest way but some complex predicates might not work so for those you should directly set a NSPredicate.

You can also set this properties with a bool object which means the value of the property will not change unless manually set.

To get the evaluated boolean value the isHidden method should be called. It will not re-evaluate the predicate each time it gets called but just when the value (or hidden/disabled status) of the rows it depends on changes. When this happens and the return value changes, it will automagically reflect that change on the form so that no other method must be called.

Here is another example, this time a bit more complex:

Screenshot of hiding rows

Disabling rows (set to read-only mode)

Rows can be disabled so that the user can not change them. By default disabled rows have a gray text color. To disable a row the only thing that has to be done is setting its disabled property:

@property id disabled;

This property expects a NSNumber containing a BOOL, a NSString or a NSPredicate. A bool will statically disable (or enable the row). The other two work just like the hidden property explained in the section above. This means a row can be disabled and enabled depending on the values of other rows. When a NSString is set, a NSPredicate will be generated taking the string as format string so that it has to be consistent for that purpose.

A difference to the hidden property is that checking the disabled status of a row does not automatically reflect that value on the form. Tharefore, the XLFormViewController's updateFormRow method should be called.

Validations

We can validate the form data using XLForm validation support.

Each XLFormRowDescriptor instance contains a list of validators. We can add validators, remove validators and validate a particular row using these methods:

-(void)addValidator:(id<XLFormValidatorProtocol>)validator;
-(void)removeValidator:(id<XLFormValidatorProtocol>)validator;
-(XLFormValidationStatus *)doValidation;

We can define our own custom validators just defining a object that conforms to XLFormValidatorProtocol.

@protocol XLFormValidatorProtocol <NSObject>

@required

-(XLFormValidationStatus *)isValid:(XLFormRowDescriptor *)row;

@end

XLFormRegexValidator is an example of a validator we can create.

A very common validation is ensuring that a value is not empty or nil. XLFom exposes required XLFormRowDescriptor property to specify required rows.

To get all rows validation errors we can invoke the following XLFormViewController method:

-(NSArray *)formValidationErrors;

Additional configuration of Rows

XLFormRowDescriptor allow us to configure generic aspects of a UITableViewCell, for example: the rowType, the label, the value (default value), if the cell is required, hidden or disabled, and so on.

You may want to set up another properties of the UITableViewCell. To set up another properties XLForm makes use of Key-Value Coding allowing the developer to set the cell properties by keyPath.

You just have to add the properties to cellConfig or cellConfigAtConfigure dictionary property of XLFormRowDescriptor. The main difference between cellConfig and cellConfigAtConfigure is the time when the property is set up. cellConfig properties are set up each time a cell is about to be displayed. cellConfigAtConfigure, on the other hand, set up the property just after the init method of the cell is called and only one time.

Since version 3.3.0 you can also use cellConfigForSelector to configure how the cells of the XLFormOptionsViewController look like when it is shown for a selector row.

For instance if you want to set up the placeholder you can do the following:

row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfigAtConfigure setObject:@"Title" forKey:@"textField.placeholder"];
[section addFormRow:row];

Let's see how to change the color of the cell label:

Objective C

row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfig setObject:[UIColor redColor] forKey:@"textLabel.textColor"];
[section addFormRow:row];

Swift

row = XLFormRowDescriptor(tag: "title", rowType: XLFormRowDescriptorTypeText, title: "title")
row.cellConfig.setObject(UIColor.blackColor(), forKey: "backgroundColor")
row.cellConfig.setObject(UIColor.whiteColor(), forKey: "textLabel.textColor")
section.addFormRow(row)

FAQ

How to customize the header and/or footer of a section

For this you should use the UITableViewDelegate methods in your XLFormViewController. This means you should implement one or both of these:

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section

Also you might want to implement the following methods to specify the height for these views:

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section

How to assign the first responder on form appearance

Assign the first responder when the form is shown is as simple as setting the property assignFirstResponderOnShow to YES. By default the value of the property is NO.

@property (nonatomic) BOOL assignFirstResponderOnShow;

How to set a value to a row.

You should set the value property of XLFormRowDescriptor relevant instance.

@property (nonatomic) id value;

You may notice that the value property type is id and you are the responsable to set a value with the proper type. For instance, you should set a NSString value to a XLFormRowDescriptor instance of XLFormRowDescriptorTypeText.

You may have to update the cell to see the UI changes if the row is already presented. -(void)reloadFormRow:(XLFormRowDescriptor *)formRow method is provided by XLFormViewController to do so.

How to set the default value to a row.

You should do the same as How to set a value to a row.

How to set up the options to a selector row.

XLForm has several types of selectors rows. Almost all of them need to know which are the values to be selected. For a particular XLFormRowDescriptor instance you specify these values setting a NSArray instance to selectorOptions property.

@property NSArray * selectorOptions;

How to get form values

If you want to get the raw form values you should call formValues method of XLFormDescriptor. Doing that you will get a dictionary containing all the form values. tag property value of each row is used as dictionary key. Only XLFormROwDescriptor values for non nil tag values are added to the dictionary.

You may be interested in the form values to use it as enpoint parameter. In this case httpParameters would be useful.

If you need something different, you can iterate over each row...

Objective C

 NSMutableDictionary * result = [NSMutableDictionary dictionary];
 for (XLFormSectionDescriptor * section in self.form.formSections) {
     if (!section.isMultivaluedSection){
         for (XLFormRowDescriptor * row in section.formRows) {
             if (row.tag && ![row.tag isEqualToString:@""]){
                 [result setObject:(row.value ?: [NSNull null]) forKey:row.tag];
             }
         }
     }
     else{
         NSMutableArray * multiValuedValuesArray = [NSMutableArray new];
         for (XLFormRowDescriptor * row in section.formRows) {
             if (row.value){
                 [multiValuedValuesArray addObject:row.value];
             }
         }
         [result setObject:multiValuedValuesArray forKey:section.multivaluedTag];
     }
 }
 return result;

Swift

var results = [String:String]()
if let fullName = form.formRowWithTag(tag.fullName).value as? String {
    results[tag.fullName] = fullName
}

How to change UITextField length

You can change the length of a UITextField using the cellConfigAtConfigure dictionary property. This value refers to the percentage in relation to the table view cell.

Objective C

[row.cellConfigAtConfigure setObject:[NSNumber numberWithFloat:0.7] forKey:XLFormTextFieldLengthPercentage];

Swift

row.cellConfigAtConfigure.setObject(0.7, forKey:XLFormTextFieldLengthPercentage)

**Note:**The same can be achieved for the UITextView when using XLFormRowDescriptorTypeTextView; just set your percentage for the key XLFormTextViewLengthPercentage.

How to change a UITableViewCell font

You can change the font or any other table view cell property using the cellConfig dictionary property. XLForm will set up cellConfig dictionary values when the table view cell is about to be displayed.

Objective C

[row.cellConfig setObject:[UIColor greenColor] forKey:@"textLabel.textColor"];
[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@"textLabel.font"];
[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@"detailTextLabel.font"];

Swift

row.cellConfig.setObject(UIColor.whiteColor(), forKey: "self.tintColor")
row.cellConfig.setObject(UIFont(name: "AppleSDGothicNeo-Regular", size: 17)!, forKey: "textLabel.font")
row.cellConfig.setObject(UIColor.whiteColor(), forKey: "textField.textColor")
row.cellConfig.setObject(UIFont(name: "AppleSDGothicNeo-Regular", size: 17)!, forKey: "textField.font")

For further details, please take a look at UICustomizationFormViewController.m example.

How to set min/max for date cells?

Each XLFormDateCell has a minimumDate and a maximumDate property. To set a datetime row to be a value in the next three days you would do as follows:

Objective C

[row.cellConfigAtConfigure setObject:[NSDate new] forKey:@"minimumDate"];
[row.cellConfigAtConfigure setObject:[NSDate dateWithTimeIntervalSinceNow:(60*60*24*3)] forKey:@"maximumDate"];

Swift

row.cellConfig.setObject(NSDate(), forKey: "maximumDate")

How to disable the entire form (read only mode).

disable XLFormDescriptor property can be used to disable the entire form. In order to make the displayed cell to take effect we should reload the visible cells ( [self.tableView reloadData] ). Any other row added after form disable property is set to YES will reflect the disable mode automatically (no need to reload table view).

How to hide a row or section when another rows value changes.

To hide a row or section you should set its hidden property. The easiest way of doing this is by setting a NSString to it. Let's say you want a section to hide if a previous row, which is a boolean switch, is set to 1 (or YES). Then you would do something like this:

section.hidden = [NSString stringWithFormat:@"$%@ == 1", previousRow];

That is all!

What do I have to do to migrate from version 2.2.0 to 3.0.0?

The only thing that is not compatible with older versions is that the disabled property of the XLFormRowDescriptor is an id now. So you just have to add @ before the values you set to it like this:

row.disabled = @YES; // before: row.disabled = YES;

How to change input accessory view (navigation view)

Overriding inputAccessoryViewForRowDescriptor: XLFormViewController method. If you want to disable it completely you can return nil. But you can also customize its whole appearance here.

- (UIView *)inputAccessoryViewForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor
{
      return nil; //will hide it completely
      // You can use the rowDescriptor parameter to hide/customize the accessory view for a particular rowDescriptor type.
}

How to set up a pushed view controller?

The view controller that will be pushed must conform to the XLFormRowDescriptorViewController protocol which consists of the following property:

@property (nonatomic) XLFormRowDescriptor * rowDescriptor;

This rowDescriptor refers to the selected row of the previous view controller and will be set before the transition to the new controller so that it will be accessible for example in its viewDidLoad method. That is where that view controller should be set up.

How to change the default appearance of a certain cell

The best way to do this is to extend the class of that cell and override its update and/or configure methods. To make this work you should also update the cellClassesForRowDescriptorTypes dictionary in your subclass of XLFormViewController by setting your custom class instead of the class of the cell you wanted to change.

How to change the returnKeyType of a cell

To change the returnKeyType of a cell you can set the returnKeyType and nextReturnKeyType properties. The former will be used if there is no navigation enabled or if there is no row after this row. In the other case the latter will be used. If you create a custom cell and want to use these you should conform to the XLFormReturnKeyProtocol protocol. This is how you can set them:

[row.cellConfigAtConfigure setObject:@(UIReturnKeyGo) forKey:@"nextReturnKeyType"];

How to change the height of one cell

If you want to change the height for all cells of one class you should subclass that cell and override the class method formDescriptorCellHeightForRowDescriptor. If you want to change the height of one individual cell then you can set that height to the height property of XLFormRowDescripto like this:

XLFormRowDescriptor* row = ...
row.height = 55;

How to change the appearance of the cells of a selector view controller (XLFormOptionsViewController)

To change the appearance of the cells of a XLFormOptionsViewController you can use the cellConfigForSelector property on the row descriptor. Example:

[row.cellConfigForSelector setObject:[UIColor redColor] forKey:@"textLabel.textColor"];

How to limit the characters of a XLFormTextFieldCell or a XLFormTextViewCell

You can make this happen using the textFieldMaxNumberOfCharacters and the textViewMaxNumberOfCharacters respectively.

[row.cellConfigAtConfigure setObject:@(20) forKey:@"textViewMaxNumberOfCharacters"];

Installation

Swift Package Manager

Starting with Xcode 11, Swift Package Manager is the recommended and preferred way for installing dependencies in Xcode projects. Installing dependencies via SwiftPM does not require the application nor dependencies to be written in Swift.

To add XLForm to your project using SwiftPM follow these steps:

  1. Open your project in Xcode
  2. In the main menu, select File -> Swift Packages -> Add Package Dependency...
  3. In the window, enter the package url https://github.com/xmartlabs/XLForm
  4. Configure the version to be used

To use XLForm in your code, import the module or header files as needed:

#import "XLForm.h"  // Obj-c
import XLForm       // Swift

CocoaPods

  1. Add the following line in the project's Podfile file: pod 'XLForm', '~> 4.3'.
  2. Run the command pod install from the Podfile folder directory.

XLForm has no dependencies over other pods.

How to use master branch

Often master branch contains most recent features and latest fixes. On the other hand this features was not fully tested and changes on master may occur at any time. For the previous reasons I stongly recommend to fork the repository and manage the updates from master on your own making the proper pull on demand.

To use xmartlabs master branch.....

pod 'XLForm', :git => 'https://github.com/xmartlabs/XLForm.git'

You can replace the repository URL for your forked version url if you wish.

How to use XLForm in Swift files

If you have installed XLForm with cocoapods and have set use_frameworks! in your Podfile, you can add import XLForm to any Swift file.

If you are using cocoapods but have not set use_frameworks! in your Podfile, add #import <XLForm/XLForm.h> to your bridging header file.

For further details on how to create and configure the bridging header file visit Importing Objective-C into Swift.

Carthage

In your Cartfile add:

github "xmartlabs/XLForm" ~> 4.2

Using git submodules

  • Clone XLForm as a git submodule by running the following command from your project root git folder.
$ git submodule add https://github.com/xmartlabs/XLForm.git
  • Open XLForm folder that was created by the previous git submodule command and drag the XLForm.xcodeproj into the Project Navigator of your application's Xcode project.

  • Select the XLForm.xcodeproj in the Project Navigator and verify the deployment target matches with your application deployment target.

  • Select your project in the Xcode Navigation and then select your application target from the sidebar. Next select the "General" tab and click on the + button under the "Embedded Binaries" section.

  • Select XLForm.framework and we are done!

Requirements

  • ARC
  • iOS 9.0 and above
  • Xcode 9.0+ (11.0+ for installation via Swift Package Manager)

Release Notes

Have a look at the CHANGELOG

Author

Martin Barreto (@mtnBarreto)

Contact

Any suggestion or question? Please create a Github issue or reach us out.

xmartlabs.com (@xmartlabs)

GitHub

https://github.com/xmartlabs/XLForm
Comments
  • 1. Constraints warning on running XLFormViewController

    When I am running an XLFormViewController in my app I get the below warning - I also see the same warning in the example application on the start screen.

    2015-08-07 04:11:37.102 XLForm[70659:41051330] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.

    Reviewed by sumowesley at 2015-08-07 03:13
  • 2. XLFormRowDescriptorTypeImage

    I'd like to see a XLFormRowDescriptorTypeImage which would allow the display of an image, picking an image from the photo library (options of which album), or taking a photo (options of front or rear camera).

    Reviewed by rlaferla at 2015-09-27 14:38
  • 3. UI breaks on iPad Pro in Landscape

    The width of xlform rows does not stretch to bounds of the screen, instead it centers itself. I would assume using autolayout should have mitigated this issue. Here is the screenshot of how the UI looks. screen shot 2015-09-14 at 5 33 43 pm screen shot 2015-09-14 at 5 33 57 pm

    Reviewed by shekbagg at 2015-09-14 09:36
  • 4. Add support for NSFormatter

    All the credits for this pull request are for @bhirt-bpl who made the initial PR https://github.com/xmartlabs/XLForm/pull/77 and also to @fwhenin who made an attempt https://github.com/xmartlabs/XLForm/pull/195 to rebase it 4 months ago.

    I rebased all the work from @bhirt-bpl at current master and also removed the extra commits/features @fwhenin added on his PR.

    • Added formatter related properties and methods to XLFormRowDescriptor
    • Updated XLFormTextFieldCell to use formatters when specified.
    • Created example page for formatters, FormattersViewController, and linked from OthersFormViewController
    • Added SHSPhoneComponent to Podfile to demonstrate a phone formatter on FormattersViewController.

    It will be nice if you could accept this Pull Request soon. Thanks

    Reviewed by ziogaschr at 2015-04-08 12:44
  • 5. How to get a callback from Button in a form

    I have defined a row in a form as a XLFormRowDescriptorTypeButton. This works fine and I can see and click on the button. But how do I trap the callback from the button so I can do processing?

    I see how to link to another form using: row.action.viewControllerClass = EditAccountViewController.self (in Swift)

    But I need to trap the click and do some work.

    Thanks, Keith

    Reviewed by tkeithblack at 2016-01-23 02:41
  • 6. Row show/hide causing exception in XLFormRowDescriptor.m

    I am getting an exception and I am not sure why. It is stopping on line 331 of XLFormRowDescriptor.m:

                self.hidePredicateCache = @([_hidden evaluateWithObject:self substitutionVariables:self.sectionDescriptor.formDescriptor.allRowsByTag ?: @{}]);
    

    This is in the method -(BOOL)evaluateIsHidden

    This exception is happening because I have a row hidden based on a previous row with a switch.

    This is the code causing the exception:

        actionTakenRow = [XLFormRowDescriptor formRowDescriptorWithTag:@"firstAidGiven" rowType:XLFormRowDescriptorTypeBooleanSwitch title:@"First Aid Given"];
        actionTakenRow.value = @0;
        [actionTakenSection addFormRow:actionTakenRow];
    
        actionTakenRow = [XLFormRowDescriptor formRowDescriptorWithTag:@"firstAidGivenBy" rowType:XLFormRowDescriptorTypeText];
        actionTakenRow.hidden = [NSString stringWithFormat:@"$%@==0", @"firstAidGiven"];
        [actionTakenRow.cellConfigAtConfigure setObject:@"First Aid Given By" forKey:@"textField.placeholder"];
        [actionTakenSection addFormRow:actionTakenRow];
    

    specifically this row: actionTakenRow.hidden = [NSString stringWithFormat:@"$%@==0", @"firstAidGiven"];

    Any ideas?

    Reviewed by abbeyjackson at 2015-09-04 02:29
  • 7. AFNetworking 1.3.0

    XLForm seems to be a great library. I'm not able to use it because RestKit requires AFNetworking 1.3.0 and XLForm minimal 2. Is it possible to support the older AFNetworking version, or a subversion of XLForms without the XLDataLoader?

    Reviewed by Thymo at 2014-04-30 01:05
  • 8. Add image to custom row for button

    How can I add an image to a button. In custom row, have a button "btnLocation", I use: [row.cellConfig setObject:[UIImage imageNamed:@"scan_blue"] forKey:@"btnLocation.imageView.image"];

    I konw add image to button, need use: [self.btnLocation setImage:[UIImage imageNamed:@"scan_blue"] forState:UIControlStateNormal];

    but I need use row.cellConfig to add some different image.

    Reviewed by smileat at 2017-04-12 02:17
  • 9. SubClass of XLFormViewController loaded from Nib does not show Form

    I created a FormViewController which is a subclass of XLFormViewController:

    @objc(FormViewController)
    class FormViewController: XLFormViewController {
    
      struct tag {
        static let dateTime = "dateTime"
        static let date = "date"
        static let time = "time"
      }
    
    
    
      @IBOutlet weak var myTableView: UITableView! {
        didSet {
          self.tableView = myTableView
        }
      }
    
    
      override func viewDidLoad() {
        super.viewDidLoad()
      }
    
    
      override func viewDidAppear(animated: Bool) {
    
         self.initializeForm()
      }
    
    
      func initializeForm() {
        var form : XLFormDescriptor
        var section : XLFormSectionDescriptor
        var row : XLFormRowDescriptor
    
        form = XLFormDescriptor.formDescriptorWithTitle("Dates") as! XLFormDescriptor
    
        section = XLFormSectionDescriptor.formSectionWithTitle("Inline Dates") as! XLFormSectionDescriptor
        form.addFormSection(section)
    
        // Date
        row = XLFormRowDescriptor(tag: tag.date, rowType: XLFormRowDescriptorTypeDateInline, title:"Date")
        row.value = NSDate()
        section.addFormRow(row)
    
        // Time
        row = XLFormRowDescriptor(tag: tag.time, rowType: XLFormRowDescriptorTypeTimeInline, title: "Time")
        row.value = NSDate()
        section.addFormRow(row)
    
        // DateTime
        row = XLFormRowDescriptor(tag: tag.dateTime, rowType: XLFormRowDescriptorTypeDateTimeInline, title: "Date Time")
        row.value = NSDate()
        section.addFormRow(row)
    
        self.form = form;
      }
    
    }
    

    This is from your Swift example. I also have a corresponding nib: FormViewController.xib which has a UITableView inside:

    formview_xib

    I connected the UITableView with the myTableView from above and set

    self.tableView = myTableView
    

    such that the XLFormViewController has access to the UITableView from the Nib.

    No form has been shown. Here is a screenshot:

    ios_simulator_-iphone_5-iphone_5___ios_7_1__11d167

    Reviewed by confile at 2015-04-24 18:35
  • 10. How to delete date row input which is not required?

    I used the row type of XLFormRowDescriptorTypeDateInline in my form. But I found it unable to clear the date I selected. Is there a property or a solution to handle this kind of problem? Thanks in advance if anyone could help.

    Reviewed by CaliosD at 2016-08-01 07:24
  • 11. XLFormStepCounterCell and tint

    I change my default tint colour when when the app is loaded. Unfortunately, the step counter doesn't seem to pick this up. The example below shows the step control in blue (wrong colour) and the label of the edited textfieldview below it as a green (correct colour).

    xlform_tint_example

    I have tried explicitly setting self.view.tintColor in viewDidLoad but result is the same. Just for added confirmation, buttons on the navigation bar are of the correct colour.

    Is there somewhere else I need to change the colour of the stepControl?

    Reviewed by sumowesley at 2015-08-01 21:16
  • 12. Fields are overlapping and everything appears on first row when built from Xcode 13 and Later

    When the project is build with the new Xcode version(13), The form fields appear to be overlapping and the alignment is not proper.This happens when i use Xcdoe 13 but with Xcode 12 there is no issue, But even when built with Xcode 13 or later it, it works fine in iOS 14 but not in iOS 15.

    Below is the SS when built the project with Xcode 13 and runs it on iOS 15 but not in any lower iOS versions. IMG_0434

    Below is the SS when built the project with Xcode 12 and runs it on any iOS version including iOS 15. IMG_D8069FB04439-1

    Note: I have updated the XLForm library and using the latest version but still facing this issue.

    Reviewed by bhargav500 at 2022-04-19 05:16
  • 13. Use auto layout to render XLFormDatePickerCell

    I've made a small change on the constraints used to display the datePicker in cell's contentView in order to fix a display issue of the datePicker when the picker style is set to inline.

    Reviewed by julienpouget at 2021-12-09 13:53
  • 14. How to change the section header or change tableview style

    @mats-claassen I reviewed all the docs about the framework, but cannot find a solution to change the section height . It's so normal in the project, so does it support it ? And then I want to change the TableView style.

    Reviewed by MarcSteven at 2021-07-13 01:35
  • 15. trailingSwipeActionsConfigurationForRowAtIndexPath never called

    I've implemented - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(nonnull NSIndexPath *)indexPath and swipe as much as I can but it's never called. I've tried with and without XLFormSectionOptionCanDelete on the XLFormSectionDescriptor and it makes no difference. Does XLForm do something to preclude swipe actions?

    UPDATE: If i use XLFormSectionOptionCanDelete, then when I click on the delete icon on the left side of the cell, my swipe action method IS called. But I still can't use a swipe gesture to make the swipe options appear.

    Reviewed by colinrblake at 2020-12-04 21:40
  • 16. Ability to change TextField Auto Capitalization

    I would like to be able to change the autocapitalization for row descriptor that is using a UITextField. My app crashes when I try:

    [row.cellConfigAtConfigure setObject:@(UITextAutocapitalizationTypeNone) forKey:@"textField.autocapitalizationType"]

    The error is that the autocapitalizationType property is not code-value compliant. It would be great if we could just set this property on the row descriptor directly or allow the code above to work. Or, allow us access directly to the embedded textfield.

    Reviewed by reggatta at 2020-09-09 17:57
  • 17. Crash: Inline date picker row following alert presentation. Attempted to scroll the table view to an out-of-bounds row

    Steps:

    • Open the SwiftExample project in this repo
    • Select Date & Time
    • Tap the Countdown Timer row in the INLINE DATES section to expand the picker
    • Scroll down to the DATEPICKER section and select a new date
    • Tap "OK" on the alert
    • Scroll back up to the Countdown Timer row

    Expected:

    • Countdown Timer row should display

    Actual:

    • App crashes before displaying the Countdown Timer row.
    Thread 1: Exception: "Attempted to scroll the table view to an out-of-bounds row (4) when there are only 4 rows in section 0. Table view: <UITableView: 0x7fc7fb838600; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x60000316a550>; layer = <CALayer: 0x600003f60440>; contentOffset: {0, 183.33333333333334}; contentSize: {320, 923.33333587646484}; adjustedContentInset: {64, 0, 0, 0}; dataSource: <SwiftExample.DatesFormViewController: 0x7fc7f95178e0>>"
    
    #0	0x00007fff23e3de5e in __exceptionPreprocess ()
    #1	0x00007fff512539b2 in objc_exception_throw ()
    #2	0x00007fff23e3dcac in +[NSException raise:format:] ()
    #3	0x00007fff4951cb73 in -[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:usingPresentationValues:] ()
    #4	0x00007fff4951d6dc in -[UITableView _scrollToRowAtIndexPath:atScrollPosition:animated:usingPresentationValues:] ()
    #5	0x00007fff4951d58c in -[UITableView scrollToRowAtIndexPath:atScrollPosition:animated:] ()
    #6	0x000000010ef60f97 in -[XLFormViewController ensureRowIsVisible:] at /Users/rob/Downloads/XLForm-master/XLForm/XL/Controllers/XLFormViewController.m:464
    #7	0x000000010ef26161 in -[XLFormDateCell becomeFirstResponder] at /Users/rob/Downloads/XLForm-master/XLForm/XL/Cell/XLFormDateCell.m:85
    #8	0x00007fff49816d94 in -[UIView(Hierarchy) deferredBecomeFirstResponder] ()
    #9	0x00007fff49816e38 in -[UIView(Hierarchy) _promoteSelfOrDescendantToFirstResponderIfNecessary] ()
    #10	0x00007fff498172fa in __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke ()
    #11	0x00007fff25ac3778 in -[NSISEngine withBehaviors:performModifications:] ()
    #12	0x00007fff498171b5 in -[UIView(Hierarchy) _postMovedFromSuperview:] ()
    #13	0x00007fff49826730 in -[UIView(Internal) _addSubview:positioned:relativeTo:] ()
    #14	0x00007fff495093d6 in -[UITableView _addSubview:positioned:relativeTo:] ()
    #15	0x00007fff497b8dab in -[UIScrollView _addContentSubview:atBack:] ()
    #16	0x00007fff49509124 in -[UITableView _addContentSubview:atBack:] ()
    #17	0x00007fff4952c3d1 in __53-[UITableView _configureCellForDisplay:forIndexPath:]_block_invoke ()
    #18	0x00007fff4981f654 in +[UIView(Animation) performWithoutAnimation:] ()
    #19	0x00007fff4952bb77 in -[UITableView _configureCellForDisplay:forIndexPath:] ()
    #20	0x00007fff4953decf in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] ()
    #21	0x00007fff4950740a in -[UITableView _updateVisibleCellsNow:] ()
    #22	0x00007fff4952724e in -[UITableView layoutSubviews] ()
    #23	0x00007fff4982d5f4 in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
    #24	0x00007fff2b4e9260 in -[CALayer layoutSublayers] ()
    #25	0x00007fff2b4ef3eb in CA::Layer::layout_if_needed(CA::Transaction*) ()
    #26	0x00007fff2b4faa8a in CA::Layer::layout_and_display_if_needed(CA::Transaction*) ()
    #27	0x00007fff2b443a7c in CA::Context::commit_transaction(CA::Transaction*, double) ()
    #28	0x00007fff2b477467 in CA::Transaction::commit() ()
    #29	0x00007fff2b477df9 in CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) ()
    #30	0x00007fff23da1087 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
    #31	0x00007fff23d9bb3e in __CFRunLoopDoObservers ()
    #32	0x00007fff23d9c08a in __CFRunLoopRun ()
    #33	0x00007fff23d9b8a4 in CFRunLoopRunSpecific ()
    #34	0x00007fff38c39bbe in GSEventRunModal ()
    #35	0x00007fff49325968 in UIApplicationMain ()
    #36	0x000000010eeff39b in main at /Users/rob/Downloads/XLForm-master/Examples/Swift/SwiftExample/AppDelegate.swift:12
    #37	0x00007fff520ce1fd in start ()
    

    Notes:

    • Reproduced with Xcode 11, iOS 13.6.
    • The same category of issue is plaguing our app. Somehow the table datasource is getting out of sync when an expanded picker is scrolled off screen, followed by an alert presentation.
    • I've also seen instances where the expanded picker is not in the last row of the section and the same steps cause the last row of the section to not appear (though I can't reproduce that issue in the sample app).
    • @mats-claassen any support you can offer would be greatly appreciated 🙏
    Reviewed by robmaceachern at 2020-08-31 18:55
SwiftForms is a small and lightweight library written in Swift that allows you to easily create forms.
SwiftForms is a small and lightweight library written in Swift that allows you to easily create forms.

SwiftForms is a powerful and extremely flexible library written in Swift that allows to create forms by just defining them in a couple of lines. It also provides the ability to customize cells appearance, use custom cells and define your own selector controllers.

May 11, 2022
SwiftyFORM is a lightweight iOS framework for creating forms
 SwiftyFORM is a lightweight iOS framework for creating forms

SwiftyFORM is a lightweight iOS framework for creating forms Because form code is hard to write, hard to read, hard to reason about. Has a

Apr 25, 2022
Former is a fully customizable Swift library for easy creating UITableView based form.
Former is a fully customizable Swift library for easy creating UITableView based form.

Former is a fully customizable Swift library for easy creating UITableView based form. Submitting Issues Click HERE to get started with filing a bug r

Apr 27, 2022
ObjectForm - a simple yet powerful library to build form for your class models.
ObjectForm - a simple yet powerful library to build form for your class models.

ObjectForm A simple yet powerful library to build form for your class models. Motivations I found most form libraries for swift are too complicated to

Mar 11, 2022
SherlockForms - An elegant SwiftUI Form builder to create a searchable Settings and DebugMenu screens for iOS
SherlockForms - An elegant SwiftUI Form builder to create a searchable Settings and DebugMenu screens for iOS

??️‍♂️ SherlockForms What one man can invent Settings UI, another can discover i

May 2, 2022
TCDInputView is an open source custom input view which is displayed when a text field becomes the first responder.

TCDInputView for iOS TCDInputView is an open source custom input view which is displayed when a text field becomes the first responder. Requirements T

Apr 25, 2016
Carbon🚴 A declarative library for building component-based user interfaces in UITableView and UICollectionView.
Carbon🚴 A declarative library for building component-based user interfaces in UITableView and UICollectionView.

A declarative library for building component-based user interfaces in UITableView and UICollectionView. Declarative Component-Based Non-Destructive Pr

May 12, 2022
iOS Validation Library

Honour Validation library for iOS inspired by Respect/Validation. Validator.mustBe(Uppercase()).andMust(StartsWith("F")).validate("FOOBAR") ❗ If you w

Jun 3, 2021
A rule-based validation library for Swift
A rule-based validation library for Swift

SwiftValidator Swift Validator is a rule-based validation library for Swift. Core Concepts UITextField + [Rule] + (and optional error UILabel) go into

May 17, 2022
Elegant iOS form builder in Swift
Elegant iOS form builder in Swift

Made with ❤️ by XMARTLABS. This is the re-creation of XLForm in Swift. 简体中文 Overview Contents Requirements Usage How to create a Form Getting row valu

May 20, 2022
GrouponHeader - iOS TableView Header Animation, Swift/UIKit
GrouponHeader - iOS TableView Header Animation, Swift/UIKit

GrouponHeader Description: iOS TableView Header Animation Technology: Swift, UIK

Apr 5, 2022
A framework to validate inputs of text fields and text views in a convenient way.

FormValidatorSwift The FormValidatorSwift framework allows you to validate inputs of text fields and text views in a convenient way. It has been devel

Apr 7, 2022
Discover new programming concepts and more new SwiftUI 2 features in this section

Africa-Zoo One thing is sure, we will discover new programming concepts and more new SwiftUI 2 features in this section. TOPICS ARE COVERED: JSON with

Dec 13, 2021
Declarative form building framework for iOS
Declarative form building framework for iOS

Formalist Swift framework for building forms on iOS Formalist is a Swift framework for building forms on iOS using a simple, declarative, and readable

Jan 7, 2022
iOS validation framework with form validation support

ATGValidator ATGValidator is a validation framework written to address most common issues faced while verifying user input data. You can use it to val

Mar 27, 2022
Boring-example - Using boring crate from iOS application

BoringSSL example Using boring crate from iOS application. Checkout git clone gi

Dec 31, 2021
Declarative data validation framework, written in Swift

Peppermint Introduction Requirements Installation Swift Package Manager Usage Examples Predicates Constraints Predicate Constraint Compound Constraint

May 10, 2022
Custom-TopBarController - A Custom TopBar Controller With Swift
Custom-TopBarController - A Custom TopBar Controller With Swift

TopBarController Верстка Для IPhone и IPod вертска адаптивная, для IPad frane To

Jan 11, 2022
AtomicReferenceCell - Atomic Reference Cell (Arc) for Swift

Atomic Reference Cell This project provide two structures: Arc<T> and WeakArc<T>

Jan 30, 2022