如何在 UIPickerView 中添加部分标题?

Posted

技术标签:

【中文标题】如何在 UIPickerView 中添加部分标题?【英文标题】:How do I add section titles in a UIPickerView? 【发布时间】:2016-12-29 22:12:14 【问题描述】:

这绝不是需要的东西。如果可能的话会很好。我不确定这是否可以做到。

我有一个UIPickerView,它将有 41-42 个选项。现在,我按字母顺序排列了所有选项。我希望他们被分成几组,在每组之前我希望它有一个标题。类似于 TableView 具有部分的方式,您可以为每个部分指定一个标题。我想要选择器中每个部分的标题,但我不希望它成为可选行。例如:

选择器选项: 核心(不可选择) 野蛮人(可选) 吟游诗人(可选) 更多选项 APG(不可选择) 炼金术士(可选) 骑士(可选) 更多选项 继续几个

这甚至可能吗?

【问题讨论】:

你可以这样做 您能详细说明一下吗?除了 Duncan C 建议的方法之外,还有其他方法吗?有教程链接吗? 等一下..制作一些快速示例代码 【参考方案1】:

您不需要子类化 UIPickerView 来实现这一点,而是需要仔细实现数据源和委托。

我建议你有一个实现这两者的类,作为一个测试,我这样做了:

import UIKit

class PickerViewSource: NSObject, UIPickerViewDelegate, UIPickerViewDataSource 

    init(pickerView: UIPickerView) 
        super.init()
        pickerView.dataSource = self
        pickerView.delegate = self
    

    var selected: (([String: Any]) -> Void)?

    let data = [
        ["title": "Group a", "selectable": false],
        ["title": "title a1", "selectable": true],
        ["title": "title a2", "selectable": true],
        ["title": "title a3", "selectable": true],
        ["title": "Group b", "selectable": false],
        ["title": "title b1", "selectable": true],
        ["title": "title b2", "selectable": true],
        ["title": "Group c", "selectable": false],
        ["title": "title c1", "selectable": true],
        ["title": "title c2", "selectable": true],
        ]

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

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
        return data.count
    

    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView 

        let d = data[row]
        if let selectable = d["selectable"] as? Bool, selectable == true 

            if let view = view as? ItemView, let title = d["title"] as? String
                view.label.text = title
                return view
            
            let view = ItemView()
            if let title = d["title"] as? String
                view.label.text = title                    
            
            return view
        

        if let selectable = d["selectable"] as? Bool, selectable == false 
            if let view = view as? GroupView, let title = d["title"] as? String
                view.label.text = title
                return view
            
            let view = GroupView()
            if let title = d["title"] as? String
                view.label.text = title                    
            
            return view

        
        return UIView()
    

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
        var index = row
        if let selectable = data[row]["selectable"] as? Bool, selectable == false 
            index += 1
            pickerView.selectRow(index, inComponent: 0, animated: true)
        
        selected?(data[index])
    


如你所见,这个类有一个回调var selected: (([String: Any]) -> Void)?。只有可选项目才会调用它。

ViewController 实例化源并设置回调:

class ViewController: UIViewController 

    var pickerViewSource: PickerViewSource?

    @IBOutlet weak var pickerView: UIPickerView! 
        didSet
            pickerViewSource = PickerViewSource(pickerView: pickerView)

            pickerViewSource?.selected = 
                selected in

                print(selected)
            
        
    

为了完整起见,观点:

class BaseView: UIView 
    var label: UILabel = UILabel()

    override func layoutSubviews() 
        super.layoutSubviews()

        self.addSubview(label)
        label.frame = self.bounds
    


class GroupView: BaseView 
    override func layoutSubviews() 
        super.layoutSubviews()
        label.backgroundColor = .orange
    



class ItemView: BaseView 



您可以允许选择组,但不要发送选择回调,而不是通过选择下一行来强制选择。但是为了确保触发没有设置任何可选择的内容,您应该添加一个取消选择回调。

var selectedElement: [String:Any]?

var deselected: (([String: Any]) -> Void)?

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 

    if let element = selectedElement 
        deselected?(element)
        selectedElement = nil
    

    if let selectable = data[row]["selectable"] as? Bool, selectable == true 
        let element = data[row]
        selected?(element)
        selectedElement = element
    


现在您可以使用回调 selecteddeselected 来更改您的用户界面,即启用或禁用按钮。

完整示例请参见我刚刚发布的示例代码:https://github.com/vikingosegundo/PickerWithSectionTitlesExample

【讨论】:

【参考方案2】:

编辑:正如@Duncan C 在他的回答中提到的那样。在 ios7+ 中没有内置的解决方案。我的回答提供了另一种实现目标的方法。

在 iOS 7 之前,您可以使用 showsSelectionIndicator 在选取器视图中不显示不同对象的选定状态。但是 iOS7+ 不允许这样做:

在 iOS 7 及更高版本上,您无法自定义选取器视图的选择指示器。选择指示器始终显示,因此将此属性设置为 false 无效。

相反,您可以做一些讨厌的 hack:

委托采用 UIPickerViewDelegate 协议,为每个组件的行提供内容,可以是属性字符串、纯字符串或视图,它通常会响应新的选择或取消选择。

这个简短的例子只是展示了解决方案的工作原理。要使这些选择器对象不可选择,您可以使用文档中提到的属性字符串,该字符串看起来与其他字符串不同。

如果用户尝试选择这些标题对象之一,您可以不关闭选择器视图。用户将不得不选择另一个选项,并且随着时间的推移,用户将了解到这些带有粗体字符串的对象是不可选择的。

 var datasource = ["Core", "content", "content", "content", "APG", "content", "content"]


func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? 
 print(self.datasource[row])
 switch row 
     case 0:
         // create attributed string
         let myString = datasource[row]
         let myAttribute: [String:Any] = [ NSForegroundColorAttributeName: UIColor.blue, NSUnderlineStyleAttributeName: NSUnderlineStyle.styleDouble.rawValue ]
         let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
 
         return myAttrString
     case 15:
         let myString = datasource[row]
         let myAttribute: [String:Any] = [
    NSForegroundColorAttributeName: UIColor.blue,
    NSUnderlineStyleAttributeName: NSUnderlineStyle.styleDouble.rawValue ]
         let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
 
         return myAttrString
 
     case 4:
         let myString = datasource[row]
         let myAttribute: [String:Any] = [
    NSForegroundColorAttributeName: UIColor.blue,
    NSUnderlineStyleAttributeName: NSUnderlineStyle.styleDouble.rawValue ]
         let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
         return myAttrString
     default:
         //It all depends on your datasource, but if you have 0-42 strings(core, APG included) you can just output the rest under default here
         let myString = datasource[row]
         let myAttribute: [String:Any] = [ NSForegroundColorAttributeName: UIColor.black]
         let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
         return myAttrString
 
      
 
 

它看起来不太好,但你可以自定义你想要的方式。您还可以在选取器视图对象中添加图像或 UiView。

【讨论】:

为了改善这一点,如果用户选择了带有章节标题的行,则自动将选择更新到下一行。【参考方案3】:

不,我认为标准选择器视图不提供该选项。也就是说,您也许可以继承 UIPickerView 并教您的自定义子类具有您所描述的部分分隔符。

当然可以,尽管需要做更多的工作,但可以创建自己的控件,该控件的作用类似于您描述的部分的选择器。

【讨论】:

谢谢你。现在我不想把事情复杂化。试图让我的应用程序简单。我正在学习 Xcode 和 swift。我现在可以把它放在次要位置,直到我更好地理解这门语言。与其说是必需品,不如说是奢侈品。 UIPickerViewDelegatepickerView(_:viewForRow:forComponent:reusing:) 可能会被强制为组指示符和可选项目返回不同的视图。不需要子类化。 好的,我会进一步研究。我将阅读 Apple 的文档,并寻找教程。我不确定从哪里开始寻找。这是一个很好的起点。谢谢你。 @DuncanC,请看我的回答。

以上是关于如何在 UIPickerView 中添加部分标题?的主要内容,如果未能解决你的问题,请参考以下文章

UIPickerView 编程示例?

如何在 UIPickerview 中添加一行

如何在 UIActionSheet 中添加 UIPickerView

如何在 UIAlertController 中添加 UIPickerView?

如何使用 monotouch 在 uitableviewcell 中添加 uipickerview 作为子视图?

如何在 UIPickerView 上添加 UIToolbar? (iPhone/iPad)