如何在同一视图中将多个选择器视图与不同的数据源一起使用? [英] How do I use multiple picker views with different data sources in the same view?

查看:57
本文介绍了如何在同一视图中将多个选择器视图与不同的数据源一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a view with three picker views in it. Two of the picker views have the same data, an array with the numbers 1 to 100. The third picker view has an array with a list of model railroad track manufacturers in it. I have tagged the picker views using a method I found on this site, but when I run the app, all three picker views have 1 to 100 as their data. I also control-dragged from all picker views to the yellow circle at the top of the view and clicked dataSource and delegate. How do I use multiple picker views with different data sources in one view? Also, in order to make the code run, I had to delete weak from all @IBOutlet statements relating to the picker views. Is this a bad thing to do? I am relatively new to code. Thanks.

选择器视图场景屏幕截图

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
    }
}

推荐答案

在处理带有委托和数据源的多个控件时,应考虑避免视图控制器膨胀(即,本着

When dealing with multiple controls with delegates and data sources, you should consider avoiding view controller bloat (i.e., in the spirit of the single responsibility principle) by creating separate objects for the delegates of the multiple pickers. This keeps this logic out of the view controller, itself, and avoids single cumbersome UIPickerViewDataSource and UIPickerViewDelegate methods trying to service multiple pickers with hairy if-else or switch statements.

例如,这是一个视图控制器,它具有两个拾取器的出口,但是除了为视图控制器提供代码来管理这些拾取器的dataSourcedelegate之外,您还可以为每个拾取器设置单独的对象,而视图控制器要做的就是说哪个委托对象将处理哪个选择器:

For example, here is a view controller that has outlets for two pickers, but rather than encumbering the view controller with code to manage the dataSource and delegate for these pickers, you can have separate objects for each picker, and all the view controller has to do is to say which delegate object will handle which picker:

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)")
    }

}

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

The only trick is to make sure to keep a strong reference to those delegate objects, as shown above, because the picker, itself, only has weak references to its delegate, as is best practice.

picker委托方法的实现更加简洁:

And the implementation of the picker delegate methods is much cleaner:

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]
    }
}

现在,显然,这仍然是一个简化的示例,但是美丽之处在于,随着代码变得越来越复杂,详细信息被封装在单独的对象中,而不是将所有代码都包含在单个视图控制器中.

Now, this is still, obviously, a simplified example, but the beauty is that as the code gets more complicated, the details are encapsulated within separate objects, rather than encumbering a single view controller with all the code.

如果需要,可以让视图控制器为委托/数据源对象提供字符串列表.实际上,这简化了它,因为您只需要一个类供选择器委托使用,而您只需为每个选择器实例化一个不同的类即可:

If you want, you can have the view controller provide the list of strings to the delegate/data source object. In fact, that simplifies it because you need only one class for the picker delegate, and you just instantiate a different one for each picker:

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]
    }
}

这篇关于如何在同一视图中将多个选择器视图与不同的数据源一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆