如何在同一视图中使用具有不同数据源的多个选择器视图?

我有一个视图,其中有三个选择器视图。 两个选择器视图具有相同的数据,一个数字为1到100的数组。第三个选择器视图有一个数组,其中包含模型铁路轨道制造商的列表。 我使用我在此站点上找到的方法标记了选择器视图,但是当我运行应用程序时,所有三个选择器视图都以1到100作为其数据。 我还控制 – 从所有选择器视图拖动到视图顶部的黄色圆圈,然后单击dataSourcedelegate 。 如何在一个视图中使用具有不同数据源的多个选择器视图? 另外,为了使代码运行,我不得不从与选择器视图相关的所有@IBOutlet语句中删除 。 这是件坏事吗? 我对代码比较陌生。 谢谢。

选择器视图场景屏幕截图

import UIKit class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate { //MARK: Properties @IBOutlet var layoutLengthPickerView: UIPickerView! @IBOutlet var layoutWidthPickerView: UIPickerView! @IBOutlet var trackPickerView: UIPickerView! override func viewDidLoad() { super.viewDidLoad() layoutLengthPickerView = UIPickerView() layoutWidthPickerView = UIPickerView() trackPickerView = UIPickerView() layoutLengthPickerView.tag = 0 layoutWidthPickerView.tag = 1 trackPickerView.tag = 2 } let numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100"] let manufacturers = ["Atlas True Track", "Atlas Code 100", "Atlas Code 83", "Bachmann Nickel Silver", "Bachmann Steel Alloy", "Kato", "Life-Like Trains Code 100", "LIfe-Like Trains Power-Loc", "Peco Code 100", "Peco Code 83", "Peco Code 75", "Shinohara Code 100", "Shinohara Code 70", "Walthers"] func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { if pickerView.tag == 0 { return numbers[row] } else if pickerView.tag == 1 { return numbers[row] } else if pickerView.tag == 2 { return manufacturers[row] } return "" } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { if pickerView.tag == 0 { return numbers.count } else if pickerView.tag == 1 { return numbers.count } else if pickerView.tag == 2 { return manufacturers.count } return 1 } } 

在处理具有委托和数据源的多个控件时,您应该考虑通过为多个选择器的委托创建单独的对象来避免视图控制器膨胀(即,在单一责任原则的精神中)。 这使得这个逻辑UIPickerViewDataSource视图控制器本身,并避免单个繁琐的UIPickerViewDataSourceUIPickerViewDelegate方法尝试使用毛茸茸的ifelseswitch语句为多个选择器提供服务。

例如,这里有一个视图控制器,它有两个选择器的出口,但不是用视图控制器来控制dataSourcedataSource这些选择器,你可以为每个选择器提供单独的对象,并且所有视图控制器都必须做的是说哪个委托对象将处理哪个选择器:

 class ViewController: UIViewController { @IBOutlet weak var namePicker: UIPickerView! @IBOutlet weak var numberPicker: UIPickerView! let namePickerDelegate = NamePickerDelegate() let numberPickerDelegate = NumberPickerDelegate() override func viewDidLoad() { super.viewDidLoad() namePicker.delegate = namePickerDelegate namePicker.dataSource = namePickerDelegate numberPicker.delegate = numberPickerDelegate numberPicker.dataSource = numberPickerDelegate } @IBAction func didTapButton(_ sender: Any) { let nameValue = namePicker.selectedRow(inComponent: 0) let numberValue = numberPicker.selectedRow(inComponent: 0) print("\(nameValue); \(numberValue)") } } 

唯一的技巧是确保对这些委托对象保持强引用,如上所示,因为选择器本身只对其委托具有弱引用,这是最佳实践。

并且picker委托方法的实现更加清晰:

 class NamePickerDelegate: NSObject, UIPickerViewDataSource, UIPickerViewDelegate { let names = ["Mo", "Larry", "Curley"] func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return names.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return names[row] } } class NumberPickerDelegate: NSObject, UIPickerViewDataSource, UIPickerViewDelegate { let numbers: [String] = { let formatter = NumberFormatter() formatter.numberStyle = .spellOut return (0 ..< 100).compactMap { formatter.string(for: $0) } // use `flatMap` in Xcode versions prior to 9.3 }() func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return numbers.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return numbers[row] } } 

现在,这仍然是一个简化的例子,但美妙的是,随着代码变得越来越复杂,细节被封装在单独的对象中,而不是使用所有代码来阻碍单个视图控制器。


如果需要,可以让视图控制器向委托/数据源对象提供字符串列表。 实际上,这简化了它,因为您只需要一个类用于选择器委托,并且您只需为每个选择器实例化另一个类:

 class ViewController: UIViewController { let names = ["Mo", "Larry", "Curley"] let numbers: [String] = { let formatter = NumberFormatter() formatter.numberStyle = .spellOut return (0 ..< 100).compactMap { formatter.string(for: $0) } // use `flatMap` in Xcode versions prior to 9.3 }() @IBOutlet weak var numberPickerOne: UIPickerView! @IBOutlet weak var numberPickerTwo: UIPickerView! @IBOutlet weak var namePicker: UIPickerView! lazy var numberPickerOneDelegate: PickerDelegate = PickerDelegate(strings: self.numbers) lazy var numberPickerTwoDelegate: PickerDelegate = PickerDelegate(strings: self.numbers) lazy var namePickerDelegate:PickerDelegate = PickerDelegate(strings: self.names) override func viewDidLoad() { super.viewDidLoad() numberPickerOne.delegate = numberPickerOneDelegate numberPickerOne.dataSource = numberPickerOneDelegate numberPickerTwo.delegate = numberPickerTwoDelegate numberPickerTwo.dataSource = numberPickerTwoDelegate namePicker.delegate = namePickerDelegate namePicker.dataSource = namePickerDelegate } } class PickerDelegate: NSObject, UIPickerViewDataSource, UIPickerViewDelegate { let strings: [String] init(strings: [String]) { self.strings = strings super.init() } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return strings.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return strings[row] } } 

如果IBOutlet连接,则不需要标签。 所有IBOutlet都应该是弱的,我们通常对IBOutlets使用weak(UIViewController的Childs)。这是有效的,因为只要父对象,子对象才需要存在。

如果您正在使用storyboardNib用于UIPickerView ,则无需为UIPickerView进行分配。

尝试这个:

 @IBOutlet weak var trackPickerView: UIPickerView! @IBOutlet weak var layoutWidthPickerView: UIPickerView! @IBOutlet weak var layoutLengthPickerView: UIPickerView! let numbers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100"] let manufacturers = ["Atlas True Track", "Atlas Code 100", "Atlas Code 83", "Bachmann Nickel Silver", "Bachmann Steel Alloy", "Kato", "Life-Like Trains Code 100", "LIfe-Like Trains Power-Loc", "Peco Code 100", "Peco Code 83", "Peco Code 75", "Shinohara Code 100", "Shinohara Code 70", "Walthers"] func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { if pickerView == layoutWidthPickerView || pickerView == layoutLengthPickerView { return numbers[row] } return manufacturers[row] } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { if pickerView == layoutWidthPickerView || pickerView == layoutLengthPickerView { return numbers.count } return manufacturers.count } 

输出:

截图