You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
9.9 KiB
256 lines
9.9 KiB
// |
|
// IQDropDownTextField+Picker.swift |
|
// Copyright (c) 2020-21 Iftekhar Qurashi. |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
|
// of this software and associated documentation files (the "Software"), to deal |
|
// in the Software without restriction, including without limitation the rights |
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
// copies of the Software, and to permit persons to whom the Software is |
|
// furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in |
|
// all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
// THE SOFTWARE. |
|
|
|
import UIKit |
|
|
|
// MARK: - UIPickerView data source |
|
extension IQDropDownTextField: UIPickerViewDataSource { |
|
|
|
public func numberOfComponents(in pickerView: UIPickerView) -> Int { |
|
return multiItemList.count |
|
} |
|
|
|
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { |
|
let isOptionalDropDown: Bool |
|
if component < isOptionalDropDowns.count { |
|
isOptionalDropDown = isOptionalDropDowns[component] |
|
} else if let last = isOptionalDropDowns.last { |
|
isOptionalDropDown = last |
|
} else { |
|
isOptionalDropDown = true |
|
} |
|
|
|
return multiItemList[component].count + (isOptionalDropDown ? 1 : 0) |
|
} |
|
} |
|
|
|
// MARK: UIPickerView delegate |
|
extension IQDropDownTextField: UIPickerViewDelegate { |
|
|
|
public func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { |
|
if let heightsForComponents = heightsForComponents, |
|
component < heightsForComponents.count, |
|
0 < heightsForComponents[component] { |
|
return heightsForComponents[component] |
|
} |
|
|
|
return 44 |
|
} |
|
|
|
public func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat { |
|
if let widthsForComponents = widthsForComponents, |
|
component < widthsForComponents.count, |
|
0 < widthsForComponents[component] { |
|
return widthsForComponents[component] |
|
} |
|
|
|
//Else calculating it's size. |
|
let availableWidth = (pickerView.bounds.width - 20) - 2 * CGFloat(multiItemList.count - 1) |
|
return availableWidth / CGFloat(multiItemList.count) |
|
} |
|
|
|
// swiftlint:disable function_body_length |
|
public func pickerView(_ pickerView: UIPickerView, |
|
viewForRow row: Int, forComponent component: Int, |
|
reusing view: UIView?) -> UIView { |
|
|
|
let isOptionalDropDown: Bool |
|
if component < isOptionalDropDowns.count { |
|
isOptionalDropDown = isOptionalDropDowns[component] |
|
} else if let last = isOptionalDropDowns.last { |
|
isOptionalDropDown = last |
|
} else { |
|
isOptionalDropDown = true |
|
} |
|
|
|
let row = row - (isOptionalDropDown ? 1 : 0) |
|
|
|
if row == Self.optionalItemIndex { |
|
|
|
let labelText: UILabel |
|
if let label = view as? UILabel { |
|
labelText = label |
|
} else { |
|
labelText = UILabel() |
|
labelText.textAlignment = .center |
|
labelText.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth |
|
labelText.backgroundColor = UIColor.clear |
|
labelText.backgroundColor = UIColor.clear |
|
} |
|
|
|
labelText.font = dropDownFont ?? UIFont.systemFont(ofSize: 18) |
|
labelText.textColor = dropDownTextColor ?? UIColor.black |
|
|
|
labelText.isEnabled = false |
|
labelText.text = optionalItemText |
|
|
|
return labelText |
|
|
|
} else { |
|
|
|
let viewToReturn: UIView? |
|
|
|
if component < multiItemListView.count, |
|
row < multiItemListView[component].count, |
|
let view = multiItemListView[component][row] { |
|
|
|
// Archiving and Unarchiving is necessary to copy UIView instance. |
|
let viewData: Data = NSKeyedArchiver.archivedData(withRootObject: view) |
|
viewToReturn = NSKeyedUnarchiver.unarchiveObject(with: viewData) as? UIView |
|
} else { |
|
viewToReturn = nil |
|
} |
|
|
|
if let viewToReturn = viewToReturn { |
|
return viewToReturn |
|
} else { |
|
|
|
let labelText: UILabel |
|
if let label = view as? UILabel { |
|
labelText = label |
|
} else { |
|
labelText = UILabel() |
|
labelText.textAlignment = .center |
|
labelText.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth |
|
labelText.backgroundColor = UIColor.clear |
|
labelText.backgroundColor = UIColor.clear |
|
} |
|
|
|
labelText.font = dropDownFont ?? UIFont.systemFont(ofSize: 18) |
|
labelText.textColor = dropDownTextColor ?? UIColor.black |
|
|
|
let itemList = multiItemList[component] |
|
let text = itemList[row] |
|
labelText.text = text |
|
labelText.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth |
|
labelText.numberOfLines = adjustsFontSizeToFitWidth ? 1 : 0 |
|
let canSelect: Bool |
|
if let result = dataSource?.textField(textField: self, canSelectItem: text) { |
|
canSelect = result |
|
} else { |
|
canSelect = true |
|
} |
|
labelText.isEnabled = canSelect |
|
|
|
return labelText |
|
} |
|
} |
|
} |
|
// swiftlint:enable function_body_length |
|
|
|
// swiftlint:disable cyclomatic_complexity |
|
// swiftlint:disable function_body_length |
|
public func pickerView(_ pickerView: UIPickerView, |
|
didSelectRow row: Int, inComponent component: Int) { |
|
|
|
privatePickerSelectedRows[component] = row |
|
|
|
let isOptionalDropDown: Bool |
|
if component < isOptionalDropDowns.count { |
|
isOptionalDropDown = isOptionalDropDowns[component] |
|
} else if let last = isOptionalDropDowns.last { |
|
isOptionalDropDown = last |
|
} else { |
|
isOptionalDropDown = true |
|
} |
|
|
|
let row = row - (isOptionalDropDown ? 1 : 0) |
|
|
|
if row == Self.optionalItemIndex { |
|
var selectedItems = selectedItems |
|
selectedItems[component] = nil |
|
privateSetSelectedItems(selectedItems: selectedItems, animated: false, shouldNotifyDelegate: true) |
|
} else if 0 <= row { |
|
|
|
let itemList = multiItemList[component] |
|
let text: String = itemList[row] |
|
|
|
let canSelect: Bool |
|
if let result = dataSource?.textField(textField: self, canSelectItem: text) { |
|
canSelect = result |
|
} else { |
|
canSelect = true |
|
} |
|
|
|
if canSelect { |
|
var selectedItems = selectedItems |
|
selectedItems[component] = text |
|
privateSetSelectedItems(selectedItems: selectedItems, animated: false, shouldNotifyDelegate: true) |
|
} else { |
|
|
|
let proposedSelection: IQProposedSelection |
|
if let result = dataSource?.textField(textField: self, proposedSelectionModeForItem: text) { |
|
proposedSelection = result |
|
} else { |
|
proposedSelection = .both |
|
} |
|
|
|
var aboveIndex: Int = row-1 |
|
var belowIndex: Int = row+1 |
|
|
|
if proposedSelection == .above { |
|
belowIndex = itemList.count |
|
} else if proposedSelection == .below { |
|
aboveIndex = -1 |
|
} |
|
|
|
while 0 <= aboveIndex || belowIndex < itemList.count { |
|
if 0 <= aboveIndex { |
|
let aboveText: String = itemList[aboveIndex] |
|
|
|
if let result = dataSource?.textField(textField: self, canSelectItem: aboveText), result { |
|
var selectedItems = selectedItems |
|
selectedItems[component] = aboveText |
|
privateSetSelectedItems(selectedItems: selectedItems, |
|
animated: true, |
|
shouldNotifyDelegate: true) |
|
return |
|
} |
|
|
|
aboveIndex -= 1 |
|
} |
|
|
|
if belowIndex < itemList.count { |
|
let belowText: String = itemList[belowIndex] |
|
|
|
if let result = dataSource?.textField(textField: self, canSelectItem: belowText), result { |
|
var selectedItems = selectedItems |
|
selectedItems[component] = belowText |
|
privateSetSelectedItems(selectedItems: selectedItems, |
|
animated: true, |
|
shouldNotifyDelegate: true) |
|
return |
|
} |
|
|
|
belowIndex += 1 |
|
} |
|
} |
|
|
|
var selectedRows = selectedRows |
|
selectedRows[component] = 0 |
|
setSelectedRows(rows: selectedRows, animated: true) |
|
} |
|
} |
|
} |
|
// swiftlint:enable cyclomatic_complexity |
|
// swiftlint:enable function_body_length |
|
}
|
|
|