// // PickerBase.swift // VDSSample // // Created by Matt Bruce on 8/1/22. // import Foundation import UIKit import VDS import Combine public struct PickerType : RawRepresentable, Equatable, Hashable { public var rawValue: String public init(_ rawValue: String) { self.rawValue = rawValue } public init(rawValue: String) { self.rawValue = rawValue } public static var surface = PickerType(rawValue: "surface") public static var textPosition = PickerType(rawValue: "textPosition") public static var textSize = PickerType(rawValue: "textSize") public static var fontCategory = PickerType(rawValue: "fontCategory") } public protocol PickerViewable: UIPickerViewDataSource, UIPickerViewDelegate, Hashable { associatedtype EnumType: RawRepresentable var items: [EnumType] { get set } var onPickerDidSelect: ((EnumType) -> Void)? { get set } var scrollToBottom: (()->Void)? { get set } } public class PickerSelectorView: UIStackView, PickerViewable { private weak var picker: UIPickerView? fileprivate var selectedIndex = 0 private var subscribers = Set() private var label = Label().with { $0.textStyle = .bodyLarge } public var selectedItem: EnumType { return items[selectedIndex] } private var button = Button().with { instance in instance.size = .small instance.use = .secondary instance.text = "Select" } public var text: String = "" { didSet { label.text = text updateSelectedIndex() } } public var items: [EnumType] { didSet { selectedIndex = 0 } } public var onClick: AnyCancellable? public var onPickerDidSelect: ((EnumType) -> Void)? public var scrollToBottom: (()->Void)? public init(title: String, picker: UIPickerView? = nil, items: [EnumType]) { self.picker = picker self.items = items super.init(frame: .zero) self.axis = .horizontal self.distribution = .fillEqually self.alignment = .fill text = title label.text = title let buttonWrapper = View() buttonWrapper.addSubview(button) buttonWrapper.height(32) button.pinTop() button.pinTrailing() button.pinBottom() button.pinLeadingGreaterThanOrEqualTo(anchor: buttonWrapper.leadingAnchor) updateSelectedIndex() addArrangedSubview(label) addArrangedSubview(buttonWrapper) button.onClick = { [weak self] _ in self?.picker?.delegate = self self?.picker?.dataSource = self self?.picker?.reloadAllComponents() self?.picker?.selectRow(self?.selectedIndex ?? 0, inComponent: 0, animated: false) self?.picker?.isHidden = false self?.scrollToBottom?() } } func updateSelectedIndex() { guard let foundTextIndex = items.firstIndex(where: { "\($0.rawValue)" == text }) else { return } selectedIndex = foundTextIndex //add one since we always have a empty space } public required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func numberOfComponents(in pickerView: UIPickerView) -> Int { 1 } public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { items.count } public func pickerView( _ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { selectedIndex = row onPickerDidSelect?(items[row]) text = "\(items[row].rawValue)" pickerView.isHidden = true } public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { var label = UILabel() if let v = view as? UILabel { label = v } label.font = TextStyle.titleMedium.font label.text = title(for: row) label.textAlignment = .center return label } public func set(item: EnumType) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { [weak self] in guard let self else { return } self.text = "\(item.rawValue)" } } private func title(for row: Int) -> String { let item = items[row] if let item = item as? CustomStringConvertible { return item.description } else if let raw = item.rawValue as? String { return raw } else { return "" } } } public class SurfacePickerSelectorView: PickerSelectorView{ init(picker: UIPickerView? = nil){ super.init(title: "light", picker: picker, items: [.light, .dark]) } required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } public class TextPositionPickerSelectorView: PickerSelectorView{ init(picker: UIPickerView? = nil){ super.init(title: "left", picker: picker, items: [.left, .right]) } required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } public class TextSizePickerSelectorView: PickerSelectorView{ init(title: String, picker: UIPickerView? = nil){ super.init(title: title, picker: picker, items: [.small, .large]) } required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } public class FontCategoryPickerSelectorView: PickerSelectorView{ init(title: String, picker: UIPickerView? = nil){ super.init(title: title, picker: picker, items: TextStyle.FontCategory.allCases) } required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }