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

时间:2017-05-25 03:16:11

标签: ios swift uipickerview

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

Picker View Scene Screen Shot

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

3 个答案:

答案 0 :(得分:4)

当处理具有委托和数据源的多个控件时,您应该考虑通过为多个选择器的委托创建单独的对象来避免视图控制器膨胀(即,在single responsibility principle的精神中)。这样可以将此逻辑保留在视图控制器本身之外,并避免单个繁琐的UIPickerViewDataSourceUIPickerViewDelegate方法尝试为多个选择器提供多毛if - else或{{1语句。

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

delegate

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

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

}

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

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

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

答案 1 :(得分:2)

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

如果您对storyboard使用NibUIPickerView,则无需为UIPickerView进行分配。

试试这个:

@IBOutlet weak var trackPickerView: UIPickerView!
@IBOutlet weak var layoutWidthPickerView: UIPickerView!
@IBOutlet weak var layoutLengthPickerView: UIPickerView!

override func viewDidLoad() {
    super.viewDidLoad()
    trackPickerView.delegate = self
    layoutWidthPickerView.delegate = self
    layoutLengthPickerView.delegate = self
}

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

}

输出:

Screenshot

答案 2 :(得分:0)

作为一种替代方法,您只能使用一个选择器视图并执行相同的操作。

@IBOutlet weak var inputBank: UITextField!    
@IBOutlet weak var inputBranch: UITextField! // Those 2 are the fields I need to set the picker view


@IBOutlet weak var inputAccountNumber: UITextField! // this is an extra textField

private var availableBankDetails: AvailableBankDetails?
private var pickerBanks: [String]?
private var pickerBranches: [String]?
private var pickerView: UIPickerView

private var profileDomain: ProfileDomain

required init?(coder aDecoder: NSCoder) {
    self.profileDomain = ProfileDomain()
    self.pickerView = UIPickerView()
    super.init(coder: aDecoder)
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.pickerView.delegate = self
    createBankPickerView()
    createBranchPickerView()
}

private func loadAvailableBankDetails(completion: @escaping (Bool) -> ()) {
    profileDomain.getAvailableBanks { (boolResponse, jsonResponseBody) in
        if boolResponse {
            self.availableBankDetails = AvailableBankDetails(availableBankDetails: jsonResponseBody)
            self.pickerBanks = self.availableBankDetails!.getAvailableBankDetails()
            completion(true)
        } else {
            self.view.makeToast(jsonResponseBody["message"].stringValue, duration: 3, position: .bottom, title: "Error")
            completion(false)
        }

    }
}

private func loadAvailableBranchDetails(bankName: String) -> [String] {
    pickerBranches = availableBankDetails?.getAvailableBranches(bank: bankName)
    return (availableBankDetails?.getAvailableBranches(bank: bankName))!
}

private func createBankPickerView() {
    loadAvailableBankDetails { (boolResponse) in
        if boolResponse {
            self.pickerView.selectRow(1, inComponent: 0, animated: true)
            self.inputBank.inputView = self.pickerView
            self.pickerView.backgroundColor = UIColor.lightGray
            self.createToolbar(inputBankOrBranch: self.inputBank)
        } else {
            print("error")
        }
    }

}

private func createBranchPickerView() {
    self.pickerView.selectRow(1, inComponent: 0, animated: true)
    self.inputBranch.inputView = self.pickerView
    self.pickerView.backgroundColor = UIColor.lightGray
    self.createToolbar(inputBankOrBranch: self.inputBranch)
}

func createToolbar(inputBankOrBranch: UITextField) {
  let toolbar = UIToolbar()
  toolbar.sizeToFit()
  toolbar.tintColor = UIColor.darkGray
  toolbar.backgroundColor = UIColor.blue
  let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(self.closePickerView))
  toolbar.setItems([doneButton], animated: false)
  toolbar.isUserInteractionEnabled = true
  inputBankOrBranch.inputAccessoryView = toolbar
}

@objc func closePickerView() {
    view.endEditing(true)
}

func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    if (inputBank.isFirstResponder) {
        return pickerBanks!.count
    } else{
        if pickerBranches == nil {
            return 0
        }
        return pickerBranches!.count
    }
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    if (inputBank.isFirstResponder){
        return pickerBanks![row]
    } else {
        return pickerBranches![row]
    }
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    if inputBank.isFirstResponder {
        inputBank.text =  pickerBanks![row]
        inputBranch.text = ""
        self.pickerBranches = loadAvailableBranchDetails(bankName: inputBank.text!)
    } else {
        if pickerBranches == nil {
            inputBranch.text = ""
        } else {
            inputBranch.text = pickerBranches![row]
        }
    }        
}

func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
    return 100.0
}

func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
    return 60.0
}

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
    }
    else{
        label = UILabel()
    }

    label.textColor = UIColor.black
    label.textAlignment = .left
    label.font = UIFont(name: "Helvetica", size: 16)



    if inputBank.isFirstResponder {
        label.text = pickerBanks![row]
    } else {
        label.text = pickerBranches![row]
    }

    return label
}