Skip to content

Instantly share code, notes, and snippets.

@Thisura98
Created March 16, 2020 11:27
Show Gist options
  • Select an option

  • Save Thisura98/e60cf95b00639cdf758a865e28444bc3 to your computer and use it in GitHub Desktop.

Select an option

Save Thisura98/e60cf95b00639cdf758a865e28444bc3 to your computer and use it in GitHub Desktop.
Creating completely dynamic forms
/**
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