diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift index 70119f06..9e34e9db 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryField.swift @@ -12,26 +12,39 @@ public typealias TextFieldAndPickerDelegate = (UITextFieldDelegate & UIPickerVie open class ItemDropdownEntryField: BaseDropdownEntryField { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + open var pickerView: UIPickerView? + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - open var pickerData: [String] = [] - open var pickerView: UIPickerView? + /// Datasource of the picker view. + open var pickerComponents: [[String]] { + dropdownModel?.options ?? [[]] + } /// When selecting for first responder, allow initial selected value to appear in empty text field. public var setInitialValueInTextField = true /// Closure passed here will run as picker changes items. - public var observeDropdownChange: ((String, String)->())? + public var observeDropdownChange: ((String, String) -> ())? /// Closure passed here will run upon dismissing the selection picker. - public var observeDropdownSelection: ((String)->())? + public var observeDropdownSelection: ((String) -> ())? - public var itemDropdownEntryFieldModel: ItemDropdownEntryFieldModel? { + public var dropdownModel: ItemDropdownEntryFieldModel? { model as? ItemDropdownEntryFieldModel } + /// The number of components available + public var componentCount: Int { + pickerComponents.count + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -44,9 +57,9 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { self.init(frame: .zero) } - @objc public convenience init(pickerData: [String]) { + @objc public convenience init(pickerComponents: [[String]]) { self.init(frame: .zero) - self.pickerData = pickerData + self.dropdownModel?.options = pickerComponents } @objc required public init?(coder: NSCoder) { @@ -76,43 +89,60 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { pickerView?.dataSource = delegate } + /// Sets the textField with the first value of the available picker data. @objc private func setInitialValueFromPicker() { - guard !pickerData.isEmpty else { return } + guard setInitialValueInTextField, + !pickerComponents.isEmpty, + let rowText = dropdownModel?.selectedRowText + else { return } - if setInitialValueInTextField, let pickerIndex = pickerView?.selectedRow(inComponent: 0) { - observeDropdownChange?(text ?? "", pickerData[pickerIndex]) - text = pickerData[pickerIndex] - itemDropdownEntryFieldModel?.selectedIndex = pickerIndex + observeDropdownChange?(text ?? "", rowText) + text = rowText + + for component in 0.. Int { 1 } + @objc public func numberOfComponents(in pickerView: UIPickerView) -> Int { componentCount } @objc public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - pickerData.count + pickerComponents[component].count } @objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { - guard !pickerData.isEmpty else { return nil } - return pickerData[row] + guard !pickerComponents.isEmpty, + !pickerComponents[component].isEmpty + else { return nil } + + return pickerComponents[component][row] } @objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { - guard !pickerData.isEmpty else { return } - observeDropdownChange?(text ?? "", pickerData[row]) - text = pickerData[row] - itemDropdownEntryFieldModel?.selectedIndex = row + guard !pickerComponents.isEmpty, + !pickerComponents[component].isEmpty, + let rowText = dropdownModel?.selectedRowText + else { return } + + observeDropdownChange?(text ?? "", rowText) + text = rowText + dropdownModel?.selectedIndicies[component] = row } } diff --git a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift index d2ab6bd8..a147e78a 100644 --- a/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atomic/Atoms/FormFields/TextFields/ItemDropdownEntryFieldModel.swift @@ -13,16 +13,46 @@ public override class var identifier: String { "dropDown" } - public var options: [String] = [] + public var options: [[String]] = [[]] + public var selectedIndicies: [Int: Int] = [:] + + @available(*, deprecated, message: "Here for backwards compatibility for when this options was a single array.") public var selectedIndex: Int? + //-------------------------------------------------- + // MARK: - Validation + //-------------------------------------------------- public override func formFieldValue() -> AnyHashable? { - guard !options.isEmpty, - let index = selectedIndex - else { return nil } - return options[index] + guard !options.isEmpty && !selectedIndicies.isEmpty else { return nil } + + return selectedRowText + } + + /// A string of the picker row concatenated by whitespace. + public var selectedRowText: String { + + var text = "" + + for i in 0.. UIKeyboardType? { + + guard let keyboardType = keyboardOverride else { return nil } + + var typeInt = 0 + + switch keyboardType { + case "asciiCapable": + typeInt = 1 // Displays a keyboard which can enter ASCII characters + + case "numbersAndPunctuation": + typeInt = 2 // Numbers and assorted punctuation. + + case "URL": + typeInt = 3 // A type optimized for URL entry (shows . / .com prominently). + + case "numberPad": + typeInt = 4 // A number pad with locale-appropriate digits (0-9, ۰-۹, ०-९, etc.). Suitable for PIN entry. + + case "phonePad": + typeInt = 5 // A phone pad (1-9, *, 0, #, with letters under the numbers). + + case "namePhonePad": + typeInt = 6 // A type optimized for entering a person's name or phone number. + + case "emailAddress": + typeInt = 7 // A type optimized for multiple email address entry (shows space @ . prominently). + + case "decimalPad": + typeInt = 8 // A number pad with a decimal point. + + case "twitter": + typeInt = 9 // A type optimized for twitter text entry (easy access to @ #) + + case "webSearch": + typeInt = 10 // A default keyboard type with URL-oriented addition (shows space . prominently). + + case "asciiCapableNumberPad": + typeInt = 11 // A number pad (0-9) that will always be ASCII digits. + + default: + typeInt = 0 // Default type for the current input method. + } + + return UIKeyboardType(rawValue: typeInt) + } + //-------------------------------------------------- // MARK: - Keys //-------------------------------------------------- @@ -43,6 +96,7 @@ case textAlignment case enabledTextColor case disabledTextColor + case keyboardOverride case type } @@ -55,6 +109,7 @@ let typeContainer = try decoder.container(keyedBy: CodingKeys.self) placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder) + keyboardOverride = try typeContainer.decodeIfPresent(String.self, forKey: .keyboardOverride) type = try typeContainer.decodeIfPresent(EntryType.self, forKey: .type) if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor) { @@ -76,6 +131,7 @@ try container.encodeIfPresent(placeholder, forKey: .placeholder) try container.encodeIfPresent(textAlignment, forKey: .textAlignment) try container.encodeIfPresent(type, forKey: .type) + try container.encodeIfPresent(keyboardOverride, forKey: .keyboardOverride) try container.encode(enabledTextColor, forKey: .enabledTextColor) try container.encode(disabledTextColor, forKey: .disabledTextColor) } diff --git a/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift b/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift index aa581ba1..ae2f236b 100644 --- a/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift +++ b/MVMCoreUI/Atomic/Molecules/Items/DropDownFilterTableViewCell.swift @@ -12,6 +12,7 @@ import UIKit //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- + let dropDown = ItemDropdownEntryField() var delegateObject: MVMCoreUIDelegateObject? var previousIndex = NSNotFound @@ -22,16 +23,16 @@ import UIKit override public func setupView() { super.setupView() - + addMolecule(dropDown) dropDown.observeDropdownChange = { [weak self] oldValue, newValue in - guard newValue != oldValue, - let self = self, - let index = self.dropDown.pickerData.firstIndex(of: newValue), - let model = self.listItemModel as? DropDownListItemModel - else { return } - + guard oldValue != newValue, + let self = self, + let index = self.dropDown.pickerComponents.first?.firstIndex(of: newValue), + let model = self.listItemModel as? DropDownListItemModel + else { return } + if self.previousIndex != NSNotFound { self.delegateObject?.moleculeDelegate?.removeMolecules(model.molecules[self.previousIndex], animation: .fade) }