Created
March 16, 2020 11:27
-
-
Save Thisura98/e60cf95b00639cdf758a865e28444bc3 to your computer and use it in GitHub Desktop.
Creating completely dynamic forms
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| Fixed identifiers for the Fields | |
| */ | |
| enum FieldID{ | |
| case unknown | |
| case country | |
| case registration | |
| case condition | |
| case quantity | |
| case year | |
| case details | |
| case dateAdded | |
| case runs | |
| case image | |
| } | |
| /** | |
| The cell type used to display the field | |
| each having their own UITableViewCell subclass | |
| */ | |
| enum FieldCellType{ | |
| case cellDisplayingText | |
| case cellDisplayingImage | |
| } | |
| /** | |
| Stores the configuration for a UIDatePicker | |
| */ | |
| struct FieldDateSelectorConfig{ | |
| var minDate: Date | |
| var maxDate: Date | |
| var dataType: UIDatePicker.Mode | |
| } | |
| /** | |
| Describes the type and range of data displayed | |
| by a field | |
| */ | |
| enum FieldDataType{ | |
| case unknown | |
| case textEntry | |
| case textListSelector(list: [String]) | |
| case numberListSelector(list: [Int]) | |
| case numberRangeSelector(start: Int, end: Int, interval: Int?) | |
| case dateSelector(config: FieldDateSelectorConfig) | |
| case imageSelector | |
| func cellType() -> FieldCellType{ | |
| switch(self){ | |
| case .imageSelector: return .cellDisplayingImage | |
| default: return .cellDisplayingText | |
| } | |
| } | |
| } | |
| /** | |
| Stores the ID, Type and current Value for the Field. | |
| */ | |
| class Field{ | |
| var id: FieldID = .unknown | |
| var dataType: FieldDataType = .unknown | |
| // Two distinct data types for the two types of cells | |
| // defined at 'FieldCellType' | |
| var displayedValue: String? = nil | |
| var displayedImage: UIImage? = nil | |
| init() {} | |
| } | |
| /** | |
| A simple field with a text label | |
| */ | |
| class TextEntryField: Field{ | |
| init(_ id: FieldID, defaultValue: String){ | |
| super.init() | |
| self.id = id | |
| self.dataType = .textEntry | |
| setText(defaultValue) | |
| } | |
| func setText(_ newValue: String){ | |
| self.displayedValue = newValue | |
| } | |
| func getText() -> String?{ | |
| return displayedValue | |
| } | |
| } | |
| /** | |
| A string list that shows a UIPickerView | |
| */ | |
| class TextListField: Field{ | |
| var defaultValueIndex: Int? = nil | |
| init(_ id: FieldID, values: [String], defaultIndex: Int){ | |
| super.init() | |
| self.id = id | |
| self.dataType = .textListSelector(list: values) | |
| self.displayedValue = values[defaultIndex] | |
| self.defaultValueIndex = defaultIndex | |
| } | |
| func getList() -> [String]{ | |
| switch(dataType){ | |
| case .textListSelector(list: let l): return l | |
| default: return [] | |
| } | |
| } | |
| func setList(_ list: [String]){ | |
| self.dataType = .textListSelector(list: list) | |
| } | |
| func setSelectedIndex(_ index: Int){ | |
| let list = getList() | |
| displayedValue = list[index] | |
| defaultValueIndex = index | |
| } | |
| func getSelectedIndex() -> Int{ | |
| return defaultValueIndex ?? 0 | |
| } | |
| } | |
| /** | |
| A number list that shows a UIPickerView | |
| */ | |
| class NumberListField: Field{ | |
| var defaultValueIndex: Int? = nil | |
| init(_ id: FieldID, values: [Int], defaultIndex: Int){ | |
| super.init() | |
| self.id = id | |
| self.dataType = .numberListSelector(list: values) | |
| self.displayedValue = values[defaultIndex].description | |
| self.defaultValueIndex = defaultIndex | |
| } | |
| func getList() -> [Int]{ | |
| switch(dataType){ | |
| case .numberListSelector(list: let l): return l | |
| default: return [] | |
| } | |
| } | |
| func setList(_ list: [Int]){ | |
| self.dataType = .numberListSelector(list: list) | |
| } | |
| func setSelectedIndex(_ index: Int){ | |
| let list = getList() | |
| displayedValue = list[index].description | |
| defaultValueIndex = index | |
| } | |
| func getSelectedIndex() -> Int{ | |
| return defaultValueIndex ?? 0 | |
| } | |
| } | |
| /** | |
| A field that shows a UIPickerView using two numbers | |
| */ | |
| class NumberRangeField: Field{ | |
| init(_ id: FieldID, start: Int, end: Int, interval: Int?){ | |
| super.init() | |
| self.id = id | |
| self.dataType = .numberRangeSelector(start: start, end: end, interval: interval) | |
| self.displayedValue = "" | |
| } | |
| } | |
| /** | |
| A field that shows a UIDatePickerView | |
| */ | |
| class DateField: Field{ | |
| var dateFormat: String{ | |
| return "dd/MM/yyyy" | |
| } | |
| init(_ id: FieldID, config: FieldDateSelectorConfig, defaultValue: Date){ | |
| super.init() | |
| self.id = id | |
| self.dataType = .dateSelector(config: config) | |
| setDate(defaultValue) | |
| } | |
| func setDate(_ newDate: Date){ | |
| let df = DateFormatter() | |
| df.dateFormat = dateFormat | |
| displayedValue = df.string(from: newDate) | |
| } | |
| func getDate() -> Date?{ | |
| guard let dv = displayedValue else { return nil } | |
| let df = DateFormatter() | |
| df.dateFormat = dateFormat | |
| return df.date(from: dv) | |
| } | |
| } | |
| /** | |
| A field that should show the photo gallery | |
| */ | |
| class ImageField: Field{ | |
| init(_ id: FieldID, image: UIImage){ | |
| super.init() | |
| self.id = id | |
| self.dataType = .imageSelector | |
| setImage(image) | |
| } | |
| func setDate(_ image: UIImage){ | |
| displayedImage = image | |
| } | |
| func getImage() -> UIImage?{ | |
| return di | |
| } | |
| } | |
| /** | |
| Describes the order in which the form fields are displayed | |
| */ | |
| class DataSource{ | |
| var fields: [Field] = [] | |
| func get(_ id: FieldID) -> Field?{ | |
| return fields.first { return $0.id == id } | |
| } | |
| func remove(_ id: FieldID){ | |
| fields.removeAll { $0.id == id } | |
| } | |
| func add(_ field: Field){ | |
| fields.append(field) | |
| } | |
| } | |
| /* | |
| ===== | |
| Initialization Example | |
| ===== | |
| */ | |
| var dataSource = DataSource() | |
| let conditionField = TextListField(.condition, values: ["Good", "Bad"], defaultIndex: 0) | |
| let detailsField = TextEntryField(.details, defaultValue: "Hello") | |
| dataSource.add(conditionField) | |
| dataSource.add(detailsField) | |
| /* | |
| ===== | |
| Data modifying Example | |
| ===== | |
| */ | |
| print("== Initial data ==") | |
| print("1.condition = \(conditionField.getList()[conditionField.getSelectedIndex()])") | |
| print("2.details = \(detailsField.getText() ?? "")") | |
| conditionField.setSelectedIndex(1) | |
| detailsField.setText("Hello World") | |
| print("") | |
| print("== Modified data ==") | |
| print("1.condition = \(conditionField.getList()[conditionField.getSelectedIndex()])") | |
| print("2.details = \(detailsField.getText() ?? "")") | |
| /* | |
| ===== | |
| UIPickerView-like Example | |
| ===== | |
| */ | |
| func showPickerWith(field: Field){ | |
| var options: [String] = [] | |
| switch(field.dataType){ | |
| case .numberListSelector(list: let l): options = l.map{ $0.description } | |
| case .textListSelector(list: let l): options = l | |
| default: assertionFailure("Incompatible field type") | |
| } | |
| print("Show UIPickerView with options... \(options)") | |
| } | |
| print("") | |
| print("== UIPickerView example ==") | |
| showPickerWith(field: conditionField) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment