![Former](https://raw.githubusercontent.com/ra1028/Former/master/Logo.png) #### Former is a fully customizable Swift library for easy creating UITableView based form. [![Swift](https://img.shields.io/badge/Swift-5.0-4BC51D.svg?style=flat)](https://developer.apple.com/swift) [![CocoaPods Shield](https://img.shields.io/cocoapods/v/Former.svg)](https://cocoapods.org/pods/Former) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![MIT License](http://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ra1028/Former/master/LICENSE) ## Submitting Issues Click [HERE](https://github.com/ra1028/Former/issues/new?template=bug_report.md) to get started with filing a bug report. Please use this template to ensure that your issue doesn't get closed due to lack of information. ## Submitting Feature Requests Click [HERE](https://github.com/ra1028/Former/issues/new?template=feature_request.md) to get started with filing a feature request. Please use this template to ensure that your feature request doesn't get denied due to lack of information. Also please keep in mind that while we desire to make this work as well as possible for everyone we won't be able to accomodate all feature requests due to lack of time or alignment with the direction of the plugin, and you may need to consider contributing even if we agree that a feature would benefit the plugin. ## Demo ## Contents * [Requirements](#Requirements) * [Installation](#installation) * [Usage](#usage) + [Simple Example](#simple-example) + [RowFormer](#rowformer) + [ViewFormer](#viewformer) + [SectionFormer](#sectionformer) + [Former](#former) + [Customizability](#customizability) * [Contributing](#contributing) * [Submitting Issues](#submitting-issues) * [Submitting Feature Requests](#submitting-feature-requests) * [License](#license) ## Requirements - Xcode 10+ - Swift 4.2+ - iOS 10.0+ _Still wanna use iOS7 and swift 2.2 or 2.3?_ -> You can use [1.4.0](https://github.com/ra1028/Former/tree/1.4.0) instead. ## Installation #### [CocoaPods](https://cocoapods.org/) Add the following line to your Podfile: ```ruby use_frameworks! target 'YOUR_TARGET_NAME' do pod 'Former' end ``` #### [Carthage](https://github.com/Carthage/Carthage) Add the following line to your Cartfile: ```ruby github "ra1028/Former" ``` ## Usage You can set the cell's appearance and events-callback at the same time. ViewController and Cell do not need to override the provided defaults. ### Simple Example ```Swift import Former final class ViewController: FormViewController { override func viewDidLoad() { super.viewDidLoad() let labelRow = LabelRowFormer() .configure { row in row.text = "Label Cell" }.onSelected { row in // Do Something } let inlinePickerRow = InlinePickerRowFormer() { $0.titleLabel.text = "Inline Picker Cell" }.configure { row in row.pickerItems = (1...5).map { InlinePickerItem(title: "Option\($0)", value: Int($0)) } }.onValueChanged { item in // Do Something } let header = LabelViewFormer() { view in view.titleLabel.text = "Label Header" } let section = SectionFormer(rowFormer: labelRow, inlinePickerRow) .set(headerViewFormer: header) former.append(sectionFormer: section) } } ``` ### RowFormer RowFormer is the base class of the class that manages the cell. A cell that is managed by the RowFormer class should conform to the corresponding protocol. Each of the RowFormer classes exposes event handling in functions named "on*" (e.g., onSelected, onValueChanged, etc...) Default provided RowFormer classes and the protocols that corresponding to it are listed below.
Demo Class Protocol Default provided cell
Free CustomRowFormer None None
LabelRowFormer LabelFormableRow FormLabelCell
TextFieldRowFormer TextFieldFormableRow FormTextFieldCell
TextViewRowFormer TextViewFormableRow FormTextViewCell
CheckRowFormer CheckFormableRow FormCheckCell
SwitchRowFormer SwitchFormableRow FormSwitchCell
StepperRowFormer StepperFormableRow FormStepperCell
SegmentedRowFormer SegmentedFormableRow FormSegmentedCell
SliderRowFormer SliderFormableRow FormSliderCell
PickerRowFormer PickerFormableRow FormPickerCell
DatePickerRowFormer DatePickerFormableRow FormDatePickerCell
SelectorPickerRowFormer SelectorPickerFormableRow FormSelectorPickerCell
SelectorDatePickerRowFormer SelectorDatePickerFormableRow FormSelectorDatePickerCell
InlinePickerRowFormer InlinePickerFormableRow FormInlinePickerCell
InlineDatePickerRowFormer InlineDatePickerFormableRow FormInlineDatePickerCell
__example with LabelRowFormer__ ```Swift let labelRow = LabelRowFormer(instantiateType: .Nib(nibName: "YourLabelCell")) { $0.titleLabel.textColor = .blackColor() }.configure { row in row.rowHeight = 44 row.text = "Label Cell" }.onSelected { row in print("\(row.text) Selected !!") } ``` __update the cell__ ```Swift row.update() row.update { row in row.text = "Updated title" } row.cellUpdate { cell in cell.titleLabel.textColor = .redColor() } ``` __get cell instance__ ```Swift let cell = row.cell print(cell.titleLabel.text) ``` __set dynamic row height__ ```Swift row.dynamicRowHeight { tableView, indexPath -> CGFloat in return 100 } ``` ### ViewFormer ViewFormer is base class of the class that manages the HeaderFooterView. A HeaderFooterView that is managed by the ViewFormer class should conform to the corresponding protocol. Default provided ViewFormer classes and the protocols that correspond to it are listed below.
Demo Class Protocol Default provided cell
Free CustomViewFormer None None
LabelViewFormer LabelFormableView FormLabelHeaderView FormLabelFooterView
__example with LabelViewFormer__ ```Swift let headerView = LabelViewFormer(instantiateType: .Nib(nibName: "YourLabelView")) { $0.titleLabel.textColor = .blackColor() }.configure { view in view.viewHeight = 30 view.text = "Label HeaderFooter View" } ``` ### SectionFormer SectionFormer is a class that represents the Section of TableView. SectionFormer can append, add, insert, remove the RowFormer and set the ViewFormer. __example__ ```Swift let section = SectionFormer(rowFormer: row1, row2, row3) .set(headerViewFormer: headerView) .set(footerViewFormer: footerView) ``` __add the cell__ ``` section.append(rowFormer: row1, row2, row3) section.add(rowFormers: rows) section.insert(rowFormer: row, toIndex: 3) section.insert(rowFormer: row, below: otherRow) // etc... ``` __remove the cell__ ```Swift section.remove(0) section.remove(0...5) section.remove(rowFormer: row) // etc... ``` __set the HeaderFooterViewe__ ```Swift section.set(headerViewFormer: headerView) section.set(footerViewFormer: footerView) ``` ### Former Former is a class that manages the entire form. Examples is below. __add the section or cell__ ```Swift former.append(sectionFormer: row) former.add(sectionFormers: rows) former.insert(sectionFormer: section, toSection: 0) former.insert(rowFormer: row, toIndexPath: indexPath) former.insert(sectionFormer: section, above: otherSection) former.insert(rowFormers: row, below: otherRow) // etc... // with animation former.insertUpdate(sectionFormer: section, toSection: 0, rowAnimation: .Automatic) former.insertUpdate(rowFormer: row, toIndexPath: indexPath, rowAnimation: .Left) former.insertUpdate(sectionFormer: section, below: otherSection, rowAnimation: .Fade) former.insertUpdate(rowFormers: rows, above: otherRow, rowAnimation: .Bottom) // etc... ``` __remove the section or cell__ ```Swift former.removeAll() former.remove(rowFormer: row1, row2) former.remove(sectionFormer: section1, section2) // etc... // with animation former.removeAllUpdate(.Fade) former.removeUpdate(sectionFormers: sections, rowAnimation: .Middle) // etc... ``` __Select and deselect the cell__ ```Swift former.select(indexPath: indexPath, animated: true, scrollPosition: .Middle) former.select(rowFormer: row, animated: true) former.deselect(true) // etc... ``` __end editing__ ```Swift former.endEditing() ``` __become editing next/previous cell__ ```Swift if former.canBecomeEditingNext() { former.becomeEditingNext() } if former.canBecomeEditingPrevious() { former.becomeEditingPrevious() } ``` __functions to setting event handling__ ```Swift public func onCellSelected(handler: (NSIndexPath -> Void)) -> Self public func onScroll(handler: ((scrollView: UIScrollView) -> Void)) -> Self public func onBeginDragging(handler: (UIScrollView -> Void)) -> Self public func willDeselectCell(handler: (NSIndexPath -> NSIndexPath?)) -> Self public func willDisplayCell(handler: (NSIndexPath -> Void)) -> Self public func willDisplayHeader(handler: (/*section:*/Int -> Void)) -> Self public func willDisplayFooter(handler: (/*section:*/Int -> Void)) -> Self public func didDeselectCell(handler: (NSIndexPath -> Void)) -> Self public func didEndDisplayingCell(handler: (NSIndexPath -> Void)) -> Self public func didEndDisplayingHeader(handler: (/*section:*/Int -> Void)) -> Self public func didEndDisplayingFooter(handler: (/*section:*/Int -> Void)) -> Self public func didHighlightCell(handler: (NSIndexPath -> Void)) -> Self public func didUnHighlightCell(handler: (NSIndexPath -> Void)) -> Self ``` ### Customizability __ViewController__ There is no need to inherit from the FormViewController class. Instead, create an instance of UITableView and Former, as in the following example. ```Swift final class YourViewController: UIViewController { private let tableView: UITableView = UITableView(frame: CGRect.zero, style: .Grouped) // It may be IBOutlet. Not forget to addSubview. private lazy var former: Former = Former(tableView: self.tableView) ... ``` __Cell__ There is likewise no need to inherit from the default provided cell class (FormLabelCell etc ...); only conform to the corresponding protocol. You can use Nibs, of course. An example with LabelRowFormer: ```Swift final class YourCell: UITableViewCell, LabelFormableRow { // MARK: LabelFormableRow func formTextLabel() -> UILabel? { return titleLabel } func formSubTextLabel() -> UILabel? { return subTitleLabel } func updateWithRowFormer(rowFormer: RowFormer) { // Do something } // MARK: UITableViewCell var titleLabel: UILabel? var subTitleLabel: UILabel? ... ``` __RowFormer__ If you want to create a custom RowFormer, make your class inherit from BaseRowFormer and comply with the Formable protocol. It must conform to ConfigurableInlineForm. In the case of InlineRowFomer, conform to the UpdatableSelectorForm case of SelectorRowFormer. Please look at the source code for details. Examples of RowFormer using cells with two UITextFields: ```Swift public protocol DoubleTextFieldFormableRow: FormableRow { func formTextField1() -> UITextField func formTextField2() -> UITextField } public final class DoubleTextFieldRowFormer : BaseRowFormer, Formable { // MARK: Public override public var canBecomeEditing: Bool { return enabled } public var text1: String? public var text2: String? public required init(instantiateType: Former.InstantiateType = .Class, cellSetup: (T -> Void)? = nil) { super.init(instantiateType: instantiateType, cellSetup: cellSetup) } public final func onText1Changed(handler: (String -> Void)) -> Self { onText1Changed = handler return self } public final func onText2Changed(handler: (String -> Void)) -> Self { onText2Changed = handler return self } open override func cellInitialized(cell: T) { super.cellInitialized(cell) cell.formTextField1().addTarget(self, action: "text1Changed:", forControlEvents: .EditingChanged) cell.formTextField2().addTarget(self, action: "text2Changed:", forControlEvents: .EditingChanged) } open override func update() { super.update() cell.selectionStyle = .None let textField1 = cell.formTextField1() let textField2 = cell.formTextField2() textField1.text = text1 textField2.text = text2 } // MARK: Private private final var onText1Changed: (String -> Void)? private final var onText2Changed: (String -> Void)? private dynamic func text1Changed(textField: UITextField) { if enabled { let text = textField.text ?? "" self.text1 = text onText1Changed?(text) } } private dynamic func text2Changed(textField: UITextField) { if enabled { let text = textField.text ?? "" self.text2 = text onText2Changed?(text) } } } ``` ## Contributing If you're interesting in helping us improve and maintain Former, it is highly encouraged that you fork the repository and submit a pull request with your updates. If you do chose to submit a pull request, please make sure to clearly document what changes you have made in the description of the PR. ## License Former is available under the MIT license. See the LICENSE file for more info.