【问题标题】:How to use two UIPickerViews in one view controller?如何在一个视图控制器中使用两个 UIPickerViews?
【发布时间】:2014-12-24 21:04:57
【问题描述】:

我在一个视图控制器中有两个UIPickerControllers。我可以让一个工作,但是当我添加第二个时,我的应用程序崩溃了。这是我用于一个选择器视图的代码:

import UIKit

class RegisterJobPosition: UIViewController, UIPickerViewDelegate {

    @IBOutlet weak var positionLabel: UILabel!

    var position = ["Lifeguard", "Instructor", "Supervisor"]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func numberOfComponentsInPickerView(PickerView: UIPickerView!) -> Int
    {
        return 1
    }

    func pickerView(pickerView: UIPickerView!, numberOfRowsInComponent component: Int) -> Int
    {
        return position.count
    }

    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!
    {
        return position[row]
    }

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {        
        positionLabel.text = position[row]
    }
}

现在,我怎样才能让第二个选择器工作?假设我的第二个选择器视图称为location(另一个称为position)。我尝试在 location 的选择器视图方法中复制代码,但它不起作用。

【问题讨论】:

  • 如果您的视图控制器是处理用户交互的唯一代表,您将需要一种方法来区分这两者。你能发布你用来区分它们的代码吗?
  • 我只是复制代码。比如在numberOfRowsInComponent方法中,我只是放了:return location.count
  • 这部分是有道理的。但是,您如何以编程方式确定要返回计数的选择器实例?如果您有两个选择器实例向您的单个视图控制器(委托)发送消息,您需要一种方法来识别选择器,以便您可以执行适当的分支逻辑并为正确的选择器返回正确的计数。
  • 啊....我无法设置“虚拟”变量来告诉应用程序我想要哪个选择器,因为我希望两者同时可用.....我还能怎么做?
  • 我写过一篇文章,通过标签属性来区分多个UIActionSheets:bit.ly/DistinguishUIActionSheetsSwift -- 虽然不完全一样,但场景是相似的。

标签: xcode swift uipickerview


【解决方案1】:

这是我的解决方案:

  • 在情节提要中,将两个 UIPickerView 实例添加到您的视图中
  • 将第一个选择器的标签设置为1,并为“属性检查器”下的第二个选择器设置2
  • control + 从每个选择器拖动到顶部的黄色视图控制器图标并选择dataSource。重复同样的选择delegate
  • UIPickerViewDataSourceUIPickerViewDelegate 添加到您的视图控制器:

    class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
    
  • 在您的视图控制器类中,为选择器创建空数组:

    var picker1Options = []
    var picker2Options = []
    
  • viewDidLoad() 中,使用您的内容填充数组:

    picker1Options = ["Option 1","Option 2","Option 3","Option 4","Option 5"]
    picker2Options = ["Item 1","Item 2","Item 3","Item 4","Item 5"]
    
  • 实现委托和数据源方法:

    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if pickerView.tag == 1 {
            return picker1Options.count
        } else {
            return picker2Options.count
        }
    }
    
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        if pickerView.tag == 1 {
            return "\(picker1Options[row])"
        } else {
            return "\(picker2Options[row])"
        }
    }
    

【讨论】:

  • 感谢标签建议,这对我使用多个选择器真的很有帮助。我现在解决了。
  • 这让我大吃一惊,在同一个 ViewController 中填充多个 PickerView 是多么困难。在 Android 上它超级简单。
  • Lazar K 这是一个非常可恶的评论。它既不简单也不复杂。这只是一个不同的策略。在 Android 上,您会(或应该)为不同的 Spinner 使用不同的适配器。实际上,您也可以在 iOS 上使用不同的数据源和委托。我实际上会在答案中提出这个建议
  • 不使用容易出错的标签,您可以在类中只使用两个选择器视图属性(例如@IBOutlet var locationPickerViewpositionPickerView)并检查委托/数据中是否有pickerView源方法等于任一属性。
  • 类似于Diavel's的回答。
【解决方案2】:

根据我在问题中的信息,我想说您需要设置数据源和委托方法来处理区分哪个选择器实例正在调用它们的能力。

选择器视图上的Using the tag property 是一种策略。

方法中应该有一些 if/else 或 switch 语句,这些语句具有不同的逻辑,具体取决于所引用的是位置还是位置选择器。

【讨论】:

  • 关于 if/else 语句,我什至如何让一个变量成为一个告诉要使用的位置或位置选择器的值?
  • 所以,如果你在storyboard的pickers上设置了tag,你可以在callback方法中测试。看到方法中的第一个参数是pickerView了吗?您可以检查 pickerView.tag 并根据标签与您的 UI 和数据数组的相关性执行正确的逻辑。
【解决方案3】:

我发现这行得通。

class SecondViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    @IBOutlet weak var textbox1: UILabel!
    @IBOutlet weak var textbox2: UILabel!

    @IBOutlet weak var dropdown1: UIPickerView!
    @IBOutlet weak var dropdown2: UIPickerView!

    var age = ["10-20", "20-30", "30-40"]
    var Gender = ["Male", "Female"]

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

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        var countrows : Int = age.count
        if pickerView == dropdown2 {
            countrows = self.Gender.count
        }

        return countrows
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if pickerView == dropdown1 {
            let titleRow = age[row]
             return titleRow
        } else if pickerView == dropdown2 {
            let titleRow = Gender[row]
            return titleRow
        }

        return ""
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if pickerView == dropdown1 {
            self.textbox1.text = self.age[row]
        } else if pickerView == dropdown2 {            
            self.textbox2.text = self.Gender[row]
        }
    }
}

【讨论】:

  • 这个方案我觉得比使用标签好很多
【解决方案4】:

我的背景是 Android,但我的回答非常 OOP。我建议创建不同的类来实现 DataSource 和 Delegate,如下所示:

class PositionDataSourceDelegate : NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
   var position = ["Lifeguard", "Instructor", "Supervisor"]
   var selectedPosition : String?

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

    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
      return position.count
    }

    func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
       return position[row]
    }

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
       selectedPosition = position[row]
    }
}

然后是另一个位置:

class LocationDataSourceDelegate : NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
   var location = ["Up", "Down", "Everywhere"]
   var selectedLocation : String?

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

    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return location.count
    }

    func pickerView(pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
         return location[row]
    }

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        selectedLocation = location[row]
   }
}

然后在您的 RegisterJobPosition 中,您需要为每个创建一个实例:

let positionDSD = PositionDataSourceDelegate()
let locationDSD = LocationDataSourceDelegate()

并像这样将它们分配给选择器:

positionPicker.dataSource = positionDSD
positionPicker.delegate = positionDSD
locationPicker.dataSource = locationDSD
locationPicker.delegate = locationDSD

您可以使用以下方式访问所选位置和位置:

positionDSD.selectedPosition 
locationDSD.selectedLocation

希望这对您和其他人有所帮助,我也希望有一些建设性的方法来解释为什么这不是“swifty”

【讨论】:

  • 这是一个很好的 OO 解决方案,但它实际上并不能在实践中工作——我是 iOS 新手,但整天都在尝试解决这个问题,在我看来 UIPickerViewDelegate /DataSource 也必须是 UIViewController。普通的 NSObject 不起作用。
  • UIPickerViewDelegateUIPickerViewDataSource 可以应用于任何NSObject。刚刚测试了它,它似乎工作正常。看不出为什么这些特定协议不适用于非 UIViewController,因为这违背了 SDK 的设计方式。将数据源分解为自己的类是一个好主意,但目前没有足够多的 iOS 开发人员这样做。
  • 如果您正在使用 Storyboards,您可以进一步采纳 Matei 的建议。将“对象”拖到您的 VC 上并输入 PositionDataSourceDelegate。在您的 VC 代码中创建一个连接到该对象的插座(打开助手故事板旁边的窗口和CTRL从故事板拖动到VC代码。您可以调用出口位置DSD)。将 Picker 的数据源和委托方法连接到对象。最后删除 let positionDSD =.. 和 positionPicker.dataSource =... positionPicker.delegate =
【解决方案5】:

我认为与 Java 不同的最大问题是 Java 很容易允许通过构造函数传递属性。例如您可以将类 LocationDataSourceDelegate 声明为泛型并将其称为 genericDataSourceDelegate 并使构造函数接受和 Array public genericDataSourceDelegate (String data[]) 并能够创建一个可以简单地创建对象的类。您只需实例化它并将 location 传递给构造函数,如 genericDataSourceDelegate (location)

您的模型存在问题,您将不得不在一个程序中创建尽可能多的委托类,这对您的编译器来说是一种压力。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多