{"id":2545,"url":"https://github.com/xmartlabs/XLForm","last_synced_at":"2025-08-03T00:32:04.569Z","repository":{"id":15841570,"uuid":"18581578","full_name":"xmartlabs/XLForm","owner":"xmartlabs","description":"XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift \u0026 Obj-C. ","archived":false,"fork":false,"pushed_at":"2024-06-03T17:03:26.000Z","size":14172,"stargazers_count":5769,"open_issues_count":181,"forks_count":952,"subscribers_count":160,"default_branch":"master","last_synced_at":"2024-12-02T18:11:14.352Z","etag":null,"topics":["dynamic-forms","forms","objective-c","swift","xlform"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"hootlex/laravel-friendships","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xmartlabs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"xmartlabs","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2014-04-09T01:50:21.000Z","updated_at":"2024-11-24T15:17:15.000Z","dependencies_parsed_at":"2024-04-24T11:01:39.531Z","dependency_job_id":"22d61a73-477f-4a14-86e1-98703641e9db","html_url":"https://github.com/xmartlabs/XLForm","commit_stats":{"total_commits":389,"total_committers":101,"mean_commits":"3.8514851485148514","dds":0.6503856041131105,"last_synced_commit":"8335af6d839cd7497569484b2a0ac8ab4cd967ad"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmartlabs%2FXLForm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmartlabs%2FXLForm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmartlabs%2FXLForm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmartlabs%2FXLForm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xmartlabs","download_url":"https://codeload.github.com/xmartlabs/XLForm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228510736,"owners_count":17931759,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dynamic-forms","forms","objective-c","swift","xlform"],"created_at":"2024-01-05T20:16:16.464Z","updated_at":"2024-12-06T18:30:35.253Z","avatar_url":"https://github.com/xmartlabs.png","language":"Objective-C","funding_links":["https://github.com/sponsors/xmartlabs"],"categories":["UI","Objective-C","UI Components"],"sub_categories":["Form \u0026 Settings","Other free courses"],"readme":"![XLForm](https://raw.githubusercontent.com/xmartlabs/XLForm/master/XLForm.png)\n\n[![Build Status](https://travis-ci.org/xmartlabs/XLForm.svg?branch=master)](https://travis-ci.org/xmartlabs/XLForm)\n\u003ca href=\"https://cocoapods.org/pods/XLForm\"\u003e\u003cimg src=\"https://img.shields.io/cocoapods/v/XLForm.svg\" alt=\"CocoaPods compatible\" /\u003e\u003c/a\u003e\n\n**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.**\n\nWe are not implementing any new features for XLForm anymore. However, if a critical issue arises we will fix it.\n\nPurpose\n--------------\n\nXLForm 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.\n\nXLForm 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.\n\n##### Let's see the iOS Calendar Event Form created using XLForm\n\n\n![Screenshot of native Calendar Event Example](Examples/Objective-C/Examples/RealExamples/XLForm.gif)\n\n\nWhat XLForm does\n----------------\n\n * Loads a form based on a declarative [*form definition*](#how-to-create-a-form \"form definition\").\n * Keeps track of definition changes on runtime to update the form interface accordingly. Further information on [*Dynamic Forms*](#dynamic-forms---how-to-change-the-form-dynamically-at-runtime \"Dynamic Forms\") section of this readme.\n * Supports multivalued sections allowing us to create, delete or reorder rows. For further details see [*Multivalued Sections*](#multivalued-sections-insert-delete-reorder-rows \"Multivalued Sections\") section bellow.\n * Supports [*custom rows definition*](#how-to-create-a-custom-row).\n * Supports custom selectors. For further details of how to define your own selectors check [*Custom selectors*](#custom-selectors---selector-row-with-a-custom-selector-view-controller \"Custom Selectors\") section out.\n * Provides several inline selectors such as date picker and picker inline selectors and brings a way to create custom inline selectors.\n * Form data validation based on form definition.\n * Ability to easily navigate among rows, fully customizable.\n * Ability to show inputAccessoryView if needed. By default a navigation input accessory view is shown.\n * Read only mode for a particular row or the entire form.\n * 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*](#make-a-row-or-section-invisible-depending-on-other-rows-values \"Using Predicates\"))\n\n\n\n## How to create a form\n\n#### Create an instance of XLFormViewController\n\n##### Swift\n\n```swift\nclass CalendarEventFormViewController : XLFormViewController {\n\n  required init(coder aDecoder: NSCoder) {\n    super.init(coder: aDecoder)\n    self.initializeForm()\n  }\n\n\n  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {\n    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)\n    self.initializeForm()\n  }\n\n  func initializeForm() {\n    // Implementation details covered in the next section.\n  }\n\n}\n\n```\n\n##### Objective-C\n\n```objc\n#import \"XLFormViewController.h\"\n\n@interface CalendarEventFormViewController: XLFormViewController\n\n@end\n```\n\n```objc\n@interface ExamplesFormViewController ()\n\n@end\n\n@implementation ExamplesFormViewController\n\n- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {\n    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];\n    if (self){\n        [self initializeForm];\n    }\n    return self;\n}\n\n- (id)initWithCoder:(NSCoder *)aDecoder {\n    self = [super initWithCoder:aDecoder];\n    if (self){\n        [self initializeForm];\n    }\n    return self;\n}\n\n- (void)initializeForm {\n  // Implementation details covered in the next section.\n}\n\n@end\n```\n\n##### Implementing the initializeForm method\n\nTo 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.\n\nTo define a form we use 3 classes:\n\n * `XLFormDescriptor`\n * `XLFormSectionDescriptor`\n * `XLFormRowDescriptor`\n\nA 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 --\u003e\u003e Sections -- \u003e\u003e Rows). The resulting table-view form's structure (sections and rows order) mirrors the definition's structure.\n\n##### Let's see an example implementation of initializeForm to define the iOS Calendar Event Form\n\n```objc\n- (void)initializeForm {\n  XLFormDescriptor * form;\n  XLFormSectionDescriptor * section;\n  XLFormRowDescriptor * row;\n\n  form = [XLFormDescriptor formDescriptorWithTitle:@\"Add Event\"];\n\n  // First section\n  section = [XLFormSectionDescriptor formSection];\n  [form addFormSection:section];\n\n  // Title\n  row = [XLFormRowDescriptor formRowDescriptorWithTag:@\"title\" rowType:XLFormRowDescriptorTypeText];\n  [row.cellConfigAtConfigure setObject:@\"Title\" forKey:@\"textField.placeholder\"];\n  [section addFormRow:row];\n\n  // Location\n  row = [XLFormRowDescriptor formRowDescriptorWithTag:@\"location\" rowType:XLFormRowDescriptorTypeText];\n  [row.cellConfigAtConfigure setObject:@\"Location\" forKey:@\"textField.placeholder\"];\n  [section addFormRow:row];\n\n  // Second Section\n  section = [XLFormSectionDescriptor formSection];\n  [form addFormSection:section];\n\n  // All-day\n  row = [XLFormRowDescriptor formRowDescriptorWithTag:@\"all-day\" rowType:XLFormRowDescriptorTypeBooleanSwitch title:@\"All-day\"];\n  [section addFormRow:row];\n\n  // Starts\n  row = [XLFormRowDescriptor formRowDescriptorWithTag:@\"starts\" rowType:XLFormRowDescriptorTypeDateTimeInline title:@\"Starts\"];\n  row.value = [NSDate dateWithTimeIntervalSinceNow:60*60*24];\n  [section addFormRow:row];\n\n  self.form = form;\n}\n```\n\nXLForm 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.\nThat 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.\n\n**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.\n\n## Using XLForm with Storyboards\n\n* Perform the steps from **How to create a form**\n* In Interface Builder (IB), drag-and-drop a **UIViewController** onto the Storyboard\n* Associate your custom form class to the **UIViewController** using the **Identity Inspector**\n\nHow to run XLForm examples\n---------------------------------\n\n1. Clone the repository `git@github.com: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.\n2. Move to either the Objective-c or Swift [example folder](/Examples).\n3. Install example project cocoapod dependencies. From inside Objective-c or Swift example folder run `pod install`.\n4. Open XLForm or SwiftExample workspace using XCode and run the project. Enjoy!\n\n\n\nRows\n---------------------\n#### Input Rows\n\n![Screenshot of Input Examples](Examples/Objective-C/Examples/Inputs/XLForm-Inputs.gif)\n\n\n\nInput 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.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeText = @\"text\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeDefault`, `UITextAutocapitalizationTypeSentences` and `UIKeyboardTypeDefault`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeName = @\"name\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeNo`, `UITextAutocapitalizationTypeWords` and `UIKeyboardTypeDefault`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeURL = @\"url\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeNo`, `UITextAutocapitalizationTypeNone` and `UIKeyboardTypeURL`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeEmail = @\"email\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeNo`, `UITextAutocapitalizationTypeNone` and `UIKeyboardTypeEmailAddress`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypePassword = @\"password\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeNo`, `UITextAutocapitalizationTypeNone` and `UIKeyboardTypeASCIICapable`.\nThis row type also set the  `secureTextEntry` to `YES` in order to hide what the user types.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeNumber = @\"number\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeNo`, `UITextAutocapitalizationTypeNone` and `UIKeyboardTypeNumbersAndPunctuation`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypePhone = @\"phone\";\n```\nWill be represented by a `UITextField` with `UIKeyboardTypePhonePad`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeTwitter = @\"twitter\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeNo`, `UITextAutocapitalizationTypeNone` and `UIKeyboardTypeTwitter`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeAccount = @\"account\";\n```\nWill be represented by a `UITextField` with `UITextAutocorrectionTypeNo`, `UITextAutocapitalizationTypeNone` and `UIKeyboardTypeDefault`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeInteger = @\"integer\";\n```\nWill be represented by a `UITextField` with `UIKeyboardTypeNumberPad`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeDecimal = @\"decimal\";\n```\nWill be represented by a `UITextField` with `UIKeyboardTypeDecimalPad`.\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeTextView = @\"textView\";\n```\nWill be represented by a `UITextView` with `UITextAutocorrectionTypeDefault`, `UITextAutocapitalizationTypeSentences` and `UIKeyboardTypeDefault`.\n\n\n\n#### Selector Rows\n\nSelector rows allow us to select a value or values from a list. XLForm supports 8 types of selectors out of the box:\n\n![Screenshot of native Calendar Event Example](Examples/Objective-C/Examples/Selectors/XLForm-Selectors.gif)\n\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSelectorPush = @\"selectorPush\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSelectorActionSheet = @\"selectorActionSheet\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSelectorAlertView = @\"selectorAlertView\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSelectorLeftRight = @\"selectorLeftRight\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSelectorPickerView = @\"selectorPickerView\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSelectorPickerViewInline = @\"selectorPickerViewInline\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSelectorSegmentedControl = @\"selectorSegmentedControl\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeMultipleSelector = @\"multipleSelector\";\n```\n\n\nNormally 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.\n\nXLForm follows the following rules to display an object:\n\n1. If the value of the `XLFormRowDescriptor` object is nil, XLForm uses the `noValueDisplayText` row property as display text.\n2. If the XLFormRowDescriptor instance has a `valueTransformer` property value. XLForm uses the `NSValueTransformer` to convert the selected object to a NSString.\n3. If the object is a `NSString` or `NSNumber` it uses the object `description` property.\n4. If the object conforms to protocol `XLFormOptionObject`, XLForm gets the display value from `formDisplayText` method.\n5. Otherwise it return nil. That means you should conforms the protocol `:)`.\n\n\nYou 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.\n\n\nThis is the protocol declaration:\n\n```objc\n@protocol XLFormOptionObject \u003cNSObject\u003e\n\n@required\n-(NSString *)formDisplayText;\n-(id)formValue;\n\n@end\n```\n\n\n#### Date \u0026 Time Rows\n\nXLForms 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.\n\n![Screenshot of native Calendar Event Example](Examples/Objective-C/Examples/Dates/XLForm-Dates.gif)\n\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeDateInline = @\"dateInline\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeDateTimeInline = @\"datetimeInline\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeTimeInline = @\"timeInline\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeCountDownTimerInline = @\"countDownTimerInline\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeDate = @\"date\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeDateTime = @\"datetime\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeTime = @\"time\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeCountDownTimer = @\"countDownTimer\";\n```\n\nHere is an example of how to define these row types:\n\n**Objective C**\n```objc\nXLFormDescriptor * form;\nXLFormSectionDescriptor * section;\nXLFormRowDescriptor * row;\n\nform = [XLFormDescriptor formDescriptorWithTitle:@\"Dates\"];\n\nsection = [XLFormSectionDescriptor formSectionWithTitle:@\"Inline Dates\"];\n[form addFormSection:section];\n\n// Date\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:kDateInline rowType:XLFormRowDescriptorTypeDateInline title:@\"Date\"];\nrow.value = [NSDate new];\n[section addFormRow:row];\n\n// Time\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:kTimeInline rowType:XLFormRowDescriptorTypeTimeInline title:@\"Time\"];\nrow.value = [NSDate new];\n[section addFormRow:row];\n\n// DateTime\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:kDateTimeInline rowType:XLFormRowDescriptorTypeDateTimeInline title:@\"Date Time\"];\nrow.value = [NSDate new];\n[section addFormRow:row];\n\n// CountDownTimer\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:kCountDownTimerInline rowType:XLFormRowDescriptorTypeCountDownTimerInline title:@\"Countdown Timer\"];\nrow.value = [NSDate new];\n[section addFormRow:row];\n```\n\n**Swift**\n```Swift\n\nstatic let dateTime = \"dateTime\"\nstatic let date = \"date\"\nstatic let time = \"time\"\n\nvar form : XLFormDescriptor\nvar section : XLFormSectionDescriptor\nvar row : XLFormRowDescriptor\n\nform = XLFormDescriptor(title: \"Dates\") as XLFormDescriptor\n\nsection = XLFormSectionDescriptor.formSectionWithTitle(\"Inline Dates\") as XLFormSectionDescriptor\nform.addFormSection(section)\n\n// Date\nrow = XLFormRowDescriptor(tag: tag.date, rowType: XLFormRowDescriptorTypeDateInline, title:\"Date\")\nrow.value = NSDate()\nsection.addFormRow(row)\n\n// Time\nrow = XLFormRowDescriptor(tag: tag.time, rowType: XLFormRowDescriptorTypeTimeInline, title: \"Time\")\nrow.value = NSDate()\nsection.addFormRow(row)\n\n// DateTime\nrow = XLFormRowDescriptor(tag: tag.dateTime, rowType: XLFormRowDescriptorTypeDateTimeInline, title: \"Date Time\")\nrow.value = NSDate()\nsection.addFormRow(row)\n\nself.form = form;\n\n```\n#### Boolean Rows\n\nXLForms supports 2 types of boolean controls:\n\n![Screenshot of native Calendar Event Example](Examples/Objective-C/Examples/Others/XLForm-Boolean.gif)\n\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeBooleanCheck = @\"booleanCheck\";\n```\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeBooleanSwitch = @\"booleanSwitch\";\n```\n\nWe can also simulate other types of Boolean rows using any of the Selector Row Types introduced in the [Selector Rows section](#selector-rows).\n\n\n#### Other Rows\n\n##### Stepper\n\nXLForms supports counting using UIStepper control:\n\n![Screenshot of native Calendar Event Example](Examples/Objective-C/Examples/Others/XLForm-stepCounter.gif)\n\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeStepCounter = @\"stepCounter\";\n```\n\nYou can set the stepper paramaters easily:\n\n```objc\n\trow = [XLFormRowDescriptor formRowDescriptorWithTag:kStepCounter rowType:XLFormRowDescriptorTypeStepCounter title:@\"Step counter\"];\n\trow.value = @50;\n\t[row.cellConfigAtConfigure setObject:@YES forKey:@\"stepControl.wraps\"];\n\t[row.cellConfigAtConfigure setObject:@10 forKey:@\"stepControl.stepValue\"];\n\t[row.cellConfigAtConfigure setObject:@10 forKey:@\"stepControl.minimumValue\"];\n\t[row.cellConfigAtConfigure setObject:@100 forKey:@\"stepControl.maximumValue\"];\n```\n\n##### Slider\n\nXLForms supports counting using UISlider control:\n\n\n```objc\nstatic NSString *const XLFormRowDescriptorTypeSlider = @\"slider\";\n```\n\nYou can adjust the slider for your own interests very easily:\n\n```objc\n\trow = [XLFormRowDescriptor formRowDescriptorWithTag:kSlider rowType:XLFormRowDescriptorTypeSlider title:@\"Slider\"];\n\trow.value = @(30);\n\t[row.cellConfigAtConfigure setObject:@(100) forKey:@\"slider.maximumValue\"];\n\t[row.cellConfigAtConfigure setObject:@(10) forKey:@\"slider.minimumValue\"];\n\t[row.cellConfigAtConfigure setObject:@(4) forKey:@\"steps\"];\n```\n\nSet `steps` to `@(0)` to disable the steps functionality.\n\n##### Info\n\nSometimes 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.\n\n##### Button\n\nApart 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.\n\n\nMultivalued Sections (Insert, Delete, Reorder rows)\n------------------------\n\nAny `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.\nA multivalued section is just a section that support either of these modes.\n\nThe most interesting part of multivalued `XLFormSectionDescriptor` is that it supports all the types of rows that were shown on the [*Rows*](#rows \"Rows\") section as well as custom rows.\n\n![Screenshot of Multivalued Section Example](Examples/Objective-C/Examples/MultiValuedSections/XLForm-MultiValuedSections.gif)\n\n\n### How to set up a multivalued section\n\nCreating a multivalued section is as simple as use one of the following convenience `XLFormSectionDescriptor` initializer:\n\n```objc\n+(id)formSectionWithTitle:(NSString *)title\n\t\t   sectionOptions:(XLFormSectionOptions)sectionOptions;\n+(id)formSectionWithTitle:(NSString *)title\n\t\t   sectionOptions:(XLFormSectionOptions)sectionOptions\n\t\tsectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode;\n```\n\n`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.\n\n`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.\n\n\n**Let's see how to create a multivalued section**\n\n```objc\nXLFormDescriptor * form;\nXLFormSectionDescriptor * section;\nXLFormRowDescriptor * row;\n\nNSArray * nameList = @[@\"family\", @\"male\", @\"female\", @\"client\"];\n\nform = [XLFormDescriptor formDescriptorWithTitle:@\"Multivalued examples\"];\n\n// Enable Insertion, Deletion, Reordering\nsection = [XLFormSectionDescriptor formSectionWithTitle:@\"MultiValued TextField\"\n\t\t\t\t\t\t\t\t\t\t  sectionOptions:XLFormSectionOptionCanReorder | XLFormSectionOptionCanInsert | XLFormSectionOptionCanDelete];\nsection.multivaluedTag = @\"textFieldRow\";\n[form addFormSection:section];\n\nfor (NSString * tag in nameList) {\n\t// add a row to the section, each row will represent a name of the name list array.\n\trow = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];\n\t[[row cellConfig] setObject:@\"Add a new tag\" forKey:@\"textField.placeholder\"];\n\trow.value = [tag copy];\n\t[section addFormRow:row];\n}\n// add an empty row to the section.\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];\n[[row cellConfig] setObject:@\"Add a new tag\" forKey:@\"textField.placeholder\"];\n[section addFormRow:row];\n```\n\n\nForm Values\n------------------------\n\n#### formValues\n\nYou can get all form values invoking `-(NSDictionary *)formValues;` either `XLFormViewController` instance or `XLFormDescriptor` instance.\n\nThe returned `NSDictionary` is created following this rules:\n\n`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.\n\nFor 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`.\n\nFor 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']`\n\n\n#### httpParameters\n\nIn 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.\n\nWhen using `-(NSDictionary *)httpParameters` method, XLForm follows the following rules to get `XLFormRowDescriptor` value:\n\n1. If the object is a `NSString`, `NSNumber` or `NSDate`, the value is the object itself.\n2. If the object conforms to protocol `XLFormOptionObject`, XLForm gets the value from `formValue` method.\n3. Otherwise it return nil.\n\n`multivaluedTag` works in the same way as in `formValues` method.\n\n\nHow to create a Custom Row\n-------------------------------\n\nTo create a custom cell you need to create a UITableViewCell extending from `XLFormBaseCell`. `XLFormBaseCell` conforms to `XLFormDescriptorCell` protocol.\n\nYou may be interested in implement `XLFormDescriptorCell` methods to change the cell behaviour.\n\n```objc\n@protocol XLFormDescriptorCell \u003cNSObject\u003e\n\n@required\n\n@property (nonatomic, weak) XLFormRowDescriptor * rowDescriptor;\n\n// initialise all objects such as Arrays, UIControls etc...\n-(void)configure;\n// update cell when it about to be presented\n-(void)update;\n\n@optional\n\n// height of the cell\n+(CGFloat)formDescriptorCellHeightForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor;\n// called to check if cell can became first responder\n-(BOOL)formDescriptorCellCanBecomeFirstResponder;\n// called to ask cell to assign first responder to relevant UIView.\n-(BOOL)formDescriptorCellBecomeFirstResponder;\n// called when cell is selected\n-(void)formDescriptorCellDidSelectedWithFormController:(XLFormViewController *)controller;\n// http parameter name used for network request\n-(NSString *)formDescriptorHttpParameterName;\n\n// is invoked when cell becomes firstResponder, could be used for change how the cell looks like when it's the forst responder.\n-(void)highlight;\n// is invoked when cell resign firstResponder\n-(void)unhighlight;\n\n\n@end\n```\n\n\nOnce a custom cell has been created you need to let `XLForm` know about this cell by adding the row definition to `cellClassesForRowDescriptorTypes` dictionary.\n\n```objc\n[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:[MYCustomCellClass class] forKey:kMyAppCustomCellType];\n```\n\nor, in case we have used nib file to define the `XLBaseDescriptorCell`:\n\n```objc\n[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:@\"nibNameWithoutNibExtension\" forKey:kMyAppCustomCellType];\n```\n\nDoing that, XLForm will instantiate the proper cell class when kMyAppCustomCellType row type is used.\n\n\nCustom Selectors - Selector Row with a custom selector view controller\n--------------------------------------------\n\nAlmost 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.\n\nLet'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?\n\n\n![Screenshot of Map Custom Selector](Examples/Objective-C/Examples/Selectors/CustomSelectors/XLFormRowViewController/XLForm-map-custom-selector.gif)\n\n\nDefine the previous selector row is as simple as ...\n\n```objc\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorMap rowType:XLFormRowDescriptorTypeSelectorPush title:@\"Coordinate\"];\n// set up the selector controller class\nrow.action.viewControllerClass = [MapViewController class];\n// or\n//row.action.viewControllerStoryboardId = @\"MapViewControllerStoryboardId\";\n// or\n//row.action.viewControllerNibName = @\"MapViewControllerNibName\";\n\n// Set up a NSValueTransformer to convert CLLocation to NSString, it's used to show the select value description (text).\nrow.valueTransformer = [CLLocationValueTrasformer class];\n// Set up the default value\nrow.value = [[CLLocation alloc] initWithLatitude:-33 longitude:-56];\n```\n\n\n`action.viewControllerClass` controller class should conform to `XLFormRowDescriptorViewController` protocol.\n\nIn the example above, `MapViewController` conforms to `XLFormRowDescriptorViewController`.\n\n```objc\n@protocol XLFormRowDescriptorViewController \u003cNSObject\u003e\n\n@required\n@property (nonatomic) XLFormRowDescriptor * rowDescriptor;\n\n@end\n```\n\nXLForm sets up `rowDescriptor` property using the `XLFormRowDescriptor` instance that belongs to the selector row.\n\nThe 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.\n\n\u003e 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.\n\n\n#### Another example\n\n\n![Screenshot of Dynamic Custom Selector](Examples/Objective-C/Examples/Selectors/DynamicSelector/XLForm-dynamic-custom-selector.gif)\n\n\n```objc\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorUser rowType:XLFormRowDescriptorTypeSelectorPush title:@\"User\"];\nrow.action.viewControllerClass = [UsersTableViewController class];\n```\n\nYou can find the details of these examples within the example repository folder, [Examples/Objective-C/Examples/Selectors/CustomSelectors/](Examples/Objective-C/Examples/Selectors/CustomSelectors) and  [Examples/Objective-C/Examples/Selectors/DynamicSelector](Examples/Objective-C/Examples/Selectors/DynamicSelector).\n\n\n\nDynamic Forms - How to change the form dynamically at runtime\n-------------------------------\n\nAny 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.\n\nWe 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`.\n\nEach 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`.\nIt's much easier to manage `XLFormRowDescriptor`s using tags, the tag should be unique and it doesn't change on tableview additions modifications or deletions.\n\nIt'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`.\n\nUsually 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*](#make-a-row-or-section-invisible-depending-on-other-rows-values \"Using Predicates\").\n\nIn order to stay in sync with the form descriptor modifications your `XLFormViewController` subclass should override the `XLFormDescriptorDelegate` methods of 'XLFormViewController'.\n\n\u003e Note: It is important to always call the `[super ...]` method when overriding this delegate's methods.\n\n```objc\n@protocol XLFormDescriptorDelegate \u003cNSObject\u003e\n\n@required\n\n-(void)formSectionHasBeenRemoved:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;\n-(void)formSectionHasBeenAdded:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;\n-(void)formRowHasBeenAdded:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;\n-(void)formRowHasBeenRemoved:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;\n-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue;\n-(void)formRowDescriptorPredicateHasChanged:(XLFormRowDescriptor *)formRow\n                                   oldValue:(id)oldValue\n                                   newValue:(id)newValue\n                              predicateType:(XLPredicateType)predicateType;\n\n@end\n```\n\nFor instance if we want to show or hide a row depending on the value of another row:\n\n```objc\n-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue\n{\n\t// super implmentation MUST be called\n\t[super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];\n    if ([rowDescriptor.tag isEqualToString:@\"alert\"]){\n        if ([[rowDescriptor.value valueData] isEqualToNumber:@(0)] == NO \u0026\u0026 [[oldValue valueData] isEqualToNumber:@(0)]){\n            XLFormRowDescriptor * newRow = [rowDescriptor copy];\n            [newRow setTag:@\"secondAlert\"];\n            newRow.title = @\"Second Alert\";\n            [self.form addFormRow:newRow afterRow:rowDescriptor];\n        }\n        else if ([[oldValue valueData] isEqualToNumber:@(0)] == NO \u0026\u0026 [[newValue valueData] isEqualToNumber:@(0)]){\n            [self.form removeFormRowWithTag:@\"secondAlert\"];\n        }\n    }\n```\n\nMake a row or section invisible depending on other rows values\n--------------------------------\n\n### Summary\n\nXLForm 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.\nSo you can make a row invisible and visible again based on the values of other rows. The same happens with sections.\nTake a look at the following example:\n\n![Screenshot of hiding rows](Examples/Objective-C/Examples/PredicateExamples/XLFormPredicatesBasic.gif)\n\nOf 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.\n\n### How it works\n\nTo make the appearance and disappearance of rows and sections automatic, there is a property in each descriptor:\n\n```objc\n@property id hidden;\n```\n\nThis 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.\n\nFor example, you could set the following string to a row (`second`) to make it disappear when a previous row (`first`) contains the value \"hide\".\n\n```objc\nsecond.hidden = [NSString stringWithFormat:@\"$%@ contains[c] 'hide'\", first];\n```\nThis 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.\n\nWhen 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.\n\nYou can also set this properties with a bool object which means the value of the property will not change unless manually set.\n\nTo 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.\n\nHere is another example, this time a bit more complex:\n\n![Screenshot of hiding rows](Examples/Objective-C/Examples/PredicateExamples/XLFormPredicates.gif)\n\n\nDisabling rows (set to read-only mode)\n--------------------------------\n\nRows 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:\n\n```objc\n@property id disabled;\n```\nThis 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.\n\nA 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.\n\n\nValidations\n------------------------------------\n\nWe can validate the form data using XLForm validation support.\n\nEach `XLFormRowDescriptor` instance contains a list of validators. We can add validators, remove validators and validate a particular row using these methods:\n\n```objc\n-(void)addValidator:(id\u003cXLFormValidatorProtocol\u003e)validator;\n-(void)removeValidator:(id\u003cXLFormValidatorProtocol\u003e)validator;\n-(XLFormValidationStatus *)doValidation;\n```\n\nWe can define our own custom validators just defining a object that conforms to `XLFormValidatorProtocol`.\n\n```objc\n@protocol XLFormValidatorProtocol \u003cNSObject\u003e\n\n@required\n\n-(XLFormValidationStatus *)isValid:(XLFormRowDescriptor *)row;\n\n@end\n```\n\n[XLFormRegexValidator](XLForm/XL/Validation/XLFormRegexValidator.h) is an example of a validator we can create.\n\n\nA very common validation is ensuring that a value is not empty or nil. XLFom exposes `required` XLFormRowDescriptor property to specify required rows.\n\n\nTo get all rows validation errors we can invoke the following `XLFormViewController` method:\n\n```objc\n-(NSArray *)formValidationErrors;\n```\n\n\nAdditional configuration of Rows\n--------------------------------\n\n`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.\n\nYou may want to set up another properties of the `UITableViewCell`. To set up another properties `XLForm` makes use of [Key-Value Coding](https://developer.apple.com/LIBRARY/IOS/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html \"Key-Value Coding\") allowing the developer to set the cell properties by keyPath.\n\nYou just have to add the properties to `cellConfig` or `cellConfigAtConfigure` dictionary property of `XLFormRowDescriptor`.\nThe 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.\n\nSince 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.\n\nFor instance if you want to set up the placeholder you can do the following:\n\n```objc\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:@\"title\" rowType:XLFormRowDescriptorTypeText];\n[row.cellConfigAtConfigure setObject:@\"Title\" forKey:@\"textField.placeholder\"];\n[section addFormRow:row];\n```\n\nLet's see how to change the color of the cell label:\n\n**Objective C**\n\n```objc\nrow = [XLFormRowDescriptor formRowDescriptorWithTag:@\"title\" rowType:XLFormRowDescriptorTypeText];\n[row.cellConfig setObject:[UIColor redColor] forKey:@\"textLabel.textColor\"];\n[section addFormRow:row];\n```\n\n**Swift**\n```Swift\nrow = XLFormRowDescriptor(tag: \"title\", rowType: XLFormRowDescriptorTypeText, title: \"title\")\nrow.cellConfig.setObject(UIColor.blackColor(), forKey: \"backgroundColor\")\nrow.cellConfig.setObject(UIColor.whiteColor(), forKey: \"textLabel.textColor\")\nsection.addFormRow(row)\n```\n\nFAQ\n-------\n\n#### How to customize the header and/or footer of a section\n\nFor this you should use the UITableViewDelegate methods in your XLFormViewController.\nThis means you should implement one or both of these:\n\n```objc\n-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section\n\n-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section\n```\n\nAlso you might want to implement the following methods to specify the height for these views:\n\n```objc\n-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section\n\n-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section\n```\n\n#### How to assign the first responder on form appearance\n\nAssign 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`.\n\n```objc\n@property (nonatomic) BOOL assignFirstResponderOnShow;\n```\n\n#### How to set a value to a row.\n\nYou should set the `value` property of `XLFormRowDescriptor` relevant instance.\n\n```objc\n@property (nonatomic) id value;\n```\n\nYou 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`.\n\nYou may have to update the cell to see the UI changes if the row is already presented.\n`-(void)reloadFormRow:(XLFormRowDescriptor *)formRow` method is provided by `XLFormViewController` to do so.\n\n#### How to set the default value to a row.\n\nYou should do the same as [*How to set a value to a row*](#how-to-set-a-value-to-a-row \"How to set a value to a row\").\n\n\n#### How to set up the options to a selector row.\n\nXLForm 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.\n\n```objc\n@property NSArray * selectorOptions;\n```\n\n#### How to get form values\n\nIf 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.\n`tag` property value of each row is used as dictionary key. Only `XLFormROwDescriptor` values for non nil `tag` values are added to the dictionary.\n\nYou may be interested in the form values to use it as enpoint parameter. In this case `httpParameters` would be useful.\n\nIf you need something different, you can iterate over each row...\n\n**Objective C**\n```objc\n NSMutableDictionary * result = [NSMutableDictionary dictionary];\n for (XLFormSectionDescriptor * section in self.form.formSections) {\n     if (!section.isMultivaluedSection){\n         for (XLFormRowDescriptor * row in section.formRows) {\n             if (row.tag \u0026\u0026 ![row.tag isEqualToString:@\"\"]){\n                 [result setObject:(row.value ?: [NSNull null]) forKey:row.tag];\n             }\n         }\n     }\n     else{\n         NSMutableArray * multiValuedValuesArray = [NSMutableArray new];\n         for (XLFormRowDescriptor * row in section.formRows) {\n             if (row.value){\n                 [multiValuedValuesArray addObject:row.value];\n             }\n         }\n         [result setObject:multiValuedValuesArray forKey:section.multivaluedTag];\n     }\n }\n return result;\n```\n\n**Swift**\n```Swift\nvar results = [String:String]()\nif let fullName = form.formRowWithTag(tag.fullName).value as? String {\n    results[tag.fullName] = fullName\n}\n```\n\n#### How to change UITextField length\n\nYou 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.\n\n**Objective C**\n```objc\n[row.cellConfigAtConfigure setObject:[NSNumber numberWithFloat:0.7] forKey:XLFormTextFieldLengthPercentage];\n```\n**Swift**\n```Swift\nrow.cellConfigAtConfigure.setObject(0.7, forKey:XLFormTextFieldLengthPercentage)\n```\n\n**Note:**The same can be achieved for the UITextView when using `XLFormRowDescriptorTypeTextView`; just set your percentage for the key `XLFormTextViewLengthPercentage`.\n\n#### How to change a UITableViewCell font\n\nYou 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.\n\n**Objective C**\n```objc\n[row.cellConfig setObject:[UIColor greenColor] forKey:@\"textLabel.textColor\"];\n[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@\"textLabel.font\"];\n[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@\"detailTextLabel.font\"];\n```\n\n**Swift**\n```Swift\nrow.cellConfig.setObject(UIColor.whiteColor(), forKey: \"self.tintColor\")\nrow.cellConfig.setObject(UIFont(name: \"AppleSDGothicNeo-Regular\", size: 17)!, forKey: \"textLabel.font\")\nrow.cellConfig.setObject(UIColor.whiteColor(), forKey: \"textField.textColor\")\nrow.cellConfig.setObject(UIFont(name: \"AppleSDGothicNeo-Regular\", size: 17)!, forKey: \"textField.font\")\n```\n\nFor further details, please take a look at [UICustomizationFormViewController.m](/Examples/Objective-C/Examples/UICustomization/UICustomizationFormViewController.m) example.\n\n#### How to set min/max for date cells?\n\nEach 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:\n\n**Objective C**\n```objc\n[row.cellConfigAtConfigure setObject:[NSDate new] forKey:@\"minimumDate\"];\n[row.cellConfigAtConfigure setObject:[NSDate dateWithTimeIntervalSinceNow:(60*60*24*3)] forKey:@\"maximumDate\"];\n```\n\n**Swift**\n```Swift\nrow.cellConfig.setObject(NSDate(), forKey: \"maximumDate\")\n```\n\n#### How to disable the entire form (read only mode).\n\n`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] ).\nAny other row added after form `disable` property is set to `YES` will reflect the disable mode automatically (no need to reload table view).\n\n#### How to hide a row or section when another rows value changes.\n\nTo 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:\n```objc\nsection.hidden = [NSString stringWithFormat:@\"$%@ == 1\", previousRow];\n```\nThat is all!\n\n#### What do I have to do to migrate from version 2.2.0 to 3.0.0?\n\nThe 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:\n```objc\nrow.disabled = @YES; // before: row.disabled = YES;\n```\n\n#### How to change input accessory view (navigation view)\n\nOverriding `inputAccessoryViewForRowDescriptor:` `XLFormViewController` method.\nIf you want to disable it completely you can return nil. But you can also customize its whole appearance here.\n\n```obj-c\n- (UIView *)inputAccessoryViewForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor\n{\n      return nil; //will hide it completely\n      // You can use the rowDescriptor parameter to hide/customize the accessory view for a particular rowDescriptor type.\n}\n```\n\n#### How to set up a pushed view controller?\n\nThe view controller that will be pushed must conform to the `XLFormRowDescriptorViewController` protocol which consists of the following property:\n```objc\n@property (nonatomic) XLFormRowDescriptor * rowDescriptor;\n```\nThis 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.\n\n#### How to change the default appearance of a certain cell\n\nThe 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.\n\n#### How to change the returnKeyType of a cell\n\nTo 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.\nIf you create a custom cell and want to use these you should conform to the `XLFormReturnKeyProtocol` protocol.\nThis is how you can set them:\n```\n[row.cellConfigAtConfigure setObject:@(UIReturnKeyGo) forKey:@\"nextReturnKeyType\"];\n```\n\n#### How to change the height of one cell\n\nIf you want to change the height for all cells of one class you should subclass that cell and override the class method `formDescriptorCellHeightForRowDescriptor`.\nIf you want to change the height of one individual cell then you can set that height to the `height` property of XLFormRowDescripto like this:\n```\nXLFormRowDescriptor* row = ...\nrow.height = 55;\n```\n\n#### How to change the appearance of the cells of a selector view controller (XLFormOptionsViewController)\n\nTo change the appearance of the cells of a XLFormOptionsViewController you can use the `cellConfigForSelector` property on the row descriptor.\nExample:\n```\n[row.cellConfigForSelector setObject:[UIColor redColor] forKey:@\"textLabel.textColor\"];\n```\n\n#### How to limit the characters of a XLFormTextFieldCell or a XLFormTextViewCell\n\nYou can make this happen using the `textFieldMaxNumberOfCharacters` and the `textViewMaxNumberOfCharacters` respectively.\n```\n[row.cellConfigAtConfigure setObject:@(20) forKey:@\"textViewMaxNumberOfCharacters\"];\n```\n\nInstallation\n--------------------------\n\n## Swift Package Manager\n\nStarting 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.\n\nTo add XLForm to your project using SwiftPM follow these steps:\n\n1. Open your project in Xcode\n1. In the main menu, select File -\u003e Swift Packages -\u003e Add Package Dependency...\n1. In the window, enter the package url https://github.com/xmartlabs/XLForm\n1. Configure the version to be used\n\nTo use XLForm in your code, import the module or header files as needed:\n\n```objc\n#import \"XLForm.h\"  // Obj-c\n```\n\n```swift\nimport XLForm       // Swift\n```\n\n\n## CocoaPods\n\n1. Add the following line in the project's Podfile file:\n`pod 'XLForm', '~\u003e 4.3'`.\n2. Run the command `pod install` from the Podfile folder directory.\n\nXLForm **has no** dependencies over other pods.\n\n#### How to use master branch\n\nOften 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.\n\n\nTo use xmartlabs master branch.....\n\n`pod 'XLForm', :git =\u003e 'https://github.com/xmartlabs/XLForm.git'`\n\nYou can replace the repository URL for your forked version url if you wish.\n\n#### How to use XLForm in Swift files\n\nIf you have installed XLForm with cocoapods and have set `use_frameworks!` in your Podfile, you can add `import XLForm` to any Swift file.\n\nIf you are using cocoapods but have not set `use_frameworks!` in your Podfile, add `#import \u003cXLForm/XLForm.h\u003e` to your bridging header file.\n\nFor further details on how to create and configure the bridging header file visit [*Importing Objective-C into Swift*](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html \"Importing Objective-C into Swift\").\n\n\n## Carthage\n\nIn your `Cartfile` add:\n\n```\ngithub \"xmartlabs/XLForm\" ~\u003e 4.2\n```\n\n## Using git submodules\n\n* Clone XLForm as a git [submodule](http://git-scm.com/docs/git-submodule) by running the following command from your project root git folder.\n\n```bash\n$ git submodule add https://github.com/xmartlabs/XLForm.git\n```\n\n* 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.\n\n* Select the XLForm.xcodeproj in the Project Navigator and verify the deployment target matches with your application deployment target.\n\n* 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.\n\n* Select `XLForm.framework` and we are done!\n\nRequirements\n-----------------------------\n\n* ARC\n* iOS 9.0 and above\n* Xcode 9.0+ (11.0+ for installation via Swift Package Manager)\n\n\nRelease Notes\n--------------\n\nHave a look at the [CHANGELOG](https://github.com/xmartlabs/XLForm/blob/master/CHANGELOG.md)\n\nAuthor\n-----------------\n\n[Martin Barreto](https://www.github.com/mtnBarreto \"Martin Barreto Github\") ([@mtnBarreto](http://twitter.com/mtnBarreto \"@mtnBarreto\"))\n\n\nContact\n----------------\n\nAny suggestion or question? Please create a Github issue or reach us out.\n\n[xmartlabs.com](http://xmartlabs.com) ([@xmartlabs](http://twitter.com/xmartlabs \"@xmartlabs\"))\n\n[Eureka]: https://github.com/xmartlabs/Eureka\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxmartlabs%2FXLForm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxmartlabs%2FXLForm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxmartlabs%2FXLForm/lists"}