Click HERE 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.
Click HERE 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.
- 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 instead.
Add the following line to your Podfile:
use_frameworks! target 'YOUR_TARGET_NAME' do pod 'Former' endAdd the following line to your Cartfile:
github "ra1028/Former"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.
import Former final class ViewController: FormViewController { override func viewDidLoad() { super.viewDidLoad() let labelRow = LabelRowFormer<FormLabelCell>() .configure { row in row.text = "Label Cell" }.onSelected { row in // Do Something } let inlinePickerRow = InlinePickerRowFormer<FormInlinePickerCell, Int>() { $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<FormLabelHeaderView>() { view in view.titleLabel.text = "Label Header" } let section = SectionFormer(rowFormer: labelRow, inlinePickerRow) .set(headerViewFormer: header) former.append(sectionFormer: section) } }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.
example with LabelRowFormer
let labelRow = LabelRowFormer<YourLabelCell>(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
row.update() row.update { row in row.text = "Updated title" } row.cellUpdate { cell in cell.titleLabel.textColor = .redColor() }get cell instance
let cell = row.cell print(cell.titleLabel.text)set dynamic row height
row.dynamicRowHeight { tableView, indexPath -> CGFloat in return 100 }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
let headerView = LabelViewFormer<YourLabelView>(instantiateType: .Nib(nibName: "YourLabelView")) { $0.titleLabel.textColor = .blackColor() }.configure { view in view.viewHeight = 30 view.text = "Label HeaderFooter View" }SectionFormer is a class that represents the Section of TableView.
SectionFormer can append, add, insert, remove the RowFormer and set the ViewFormer.
example
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
section.remove(0) section.remove(0...5) section.remove(rowFormer: row) // etc...set the HeaderFooterViewe
section.set(headerViewFormer: headerView) section.set(footerViewFormer: footerView)Former is a class that manages the entire form.
Examples is below.
add the section or cell
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
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
former.select(indexPath: indexPath, animated: true, scrollPosition: .Middle) former.select(rowFormer: row, animated: true) former.deselect(true) // etc...end editing
former.endEditing()become editing next/previous cell
if former.canBecomeEditingNext() { former.becomeEditingNext() } if former.canBecomeEditingPrevious() { former.becomeEditingPrevious() }functions to setting event handling
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)) -> SelfViewController
There is no need to inherit from the FormViewController class.
Instead, create an instance of UITableView and Former, as in the following example.
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:
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:
public protocol DoubleTextFieldFormableRow: FormableRow { func formTextField1() -> UITextField func formTextField2() -> UITextField } public final class DoubleTextFieldRowFormer<T: UITableViewCell where T: DoubleTextFieldFormableRow> : BaseRowFormer<T>, 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) } } }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.
Former is available under the MIT license. See the LICENSE file for more info.


















