如何在 ViewController 之外符合 UIPickerViewDelegate 和 UIPickerViewDataSource?

Posted

技术标签:

【中文标题】如何在 ViewController 之外符合 UIPickerViewDelegate 和 UIPickerViewDataSource?【英文标题】:How conform to UIPickerViewDelegate and UIPickerViewDataSource outside of a ViewController? 【发布时间】:2019-07-22 03:15:10 【问题描述】:

我希望在一个场景中拥有多个 UIPickerView,而不会让我的视图控制器因委托功能而臃肿。我在 UIPickerViews 上看到的所有教程都建议使用视图控制器本身作为 UIPickerView 的委托。

如何将此委托代码移出ViewController

受this tutorial 的启发,这里有一个视图控制器,它将自己作为委托分配给它的 UIPickerViews。我对用于将行为映射到每个 UIPickerView 的 if/else 逻辑持批评态度:

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource 

    @IBOutlet weak var pickerViewA: UIPickerView!
    @IBOutlet weak var pickerViewB: UIPickerView!

    let contentsA = ["1A", "2A", "3A"]
    let contentsB = ["1B", "2B", "3B", "4B"]

    override func viewDidLoad() 
        super.viewDidLoad()
        pickerViewA.delegate   = self
        pickerViewA.dataSource = self
        pickerViewB.delegate   = self
        pickerViewB.dataSource = self
    

    func numberOfComponents(in pickerView: UIPickerView) -> Int 
        return 1 //Applies to both pickerViewA & pickerViewB
    

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
        if pickerView == pickerViewA 
            return contentsA.count
         else 
            return contentsB.count
        
    

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? 
        if pickerView == pickerViewA 
            return contentsA[row]
         else 
            return contentsB[row]
        
    

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
        if pickerView == pickerViewA 
            // Do something A
         else 
            // Do something B
        
    

理想情况下,我希望有一个符合这些协议的自定义类,然后为每个 UIPickerView 创建此类的新实例,以便它们具有唯一的委托和行为。

当我尝试定义一个名为“PickerDelegate”且符合 UIPickerViewDelegateUIPickerViewDataSource 的单独类时,我在 Xcode 中收到此错误:

类型“PickerDelegate”不符合协议“NSObjectProtocol”

显然UIPickerViewDelegate & UIPickerViewDataSource 继承自NSObjectProtocol。如果我添加协议存根(如 Xcode 所建议的那样),我会得到一长串我并不热衷于实现的功能。

有没有更简单的方法在视图控制器之外符合这些协议?

【问题讨论】:

【参考方案1】:

当我尝试定义一个名为“PickerDelegate”且符合 UIPickerViewDelegate 和 UIPickerViewDataSource 的单独类时,我在 Xcode 中收到此错误:

只需让您的 PickerDelegate 成为 NSObject 的子类。

class PickerDelegate : NSObject, UIPickerViewDelegate, UIPickerViewDataSource 

这不是强加的,而是必要的,因为 Cocoa 是 Objective-C,否则甚至无法看到您的委托和数据源方法。

【讨论】:

【参考方案2】:

你可以像这样添加扩展,并且在任何你想使用 UIpiker 的 UIviewController 中只需在视图 didload() 中设置代理

extension UIViewController: UIPickerViewDelegate, UIPickerViewDataSource 
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
        if pickerView == pickerViewA 
            return contentsA.count
         else 
            return contentsB.count
        
    

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? 
        if pickerView == pickerViewA 
            return contentsA[row]
         else 
            return contentsB[row]
        
    

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
        if pickerView == pickerViewA 
            // Do something A
         else 
            // Do something B
        
    

【讨论】:

我认为这是朝错误方向迈出的一步,因为 A) 视图控制器仍被用作委托,并且 B) 现在这些委托方法必须处理所有地方的每个 UIPickerView!意大利面!

以上是关于如何在 ViewController 之外符合 UIPickerViewDelegate 和 UIPickerViewDataSource?的主要内容,如果未能解决你的问题,请参考以下文章

类型“ViewController”不符合协议

类型 ViewController 不符合协议 SKPaymentTransactionObserver

在导航控制器之外停止 ViewController

ViewController 不符合自定义 UIButton 类

ios 8.1:类型“ViewController”不符合协议“UICollectionViewDataSource”

iOS异常:NSUnknownKeyException:“在将xib作为所有者连接到ViewController之后,”此类不符合键值的键值“