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.
257 lines
9.9 KiB
257 lines
9.9 KiB
![]()
1 year ago
|
//
|
||
|
// 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
|
||
|
}
|