在 UIPickerView 中更改选定行的颜色,滚动时也是如此

Posted

技术标签:

【中文标题】在 UIPickerView 中更改选定行的颜色,滚动时也是如此【英文标题】:Change color of selectedRow in UIPickerView, also when rolling 【发布时间】:2017-11-01 05:10:58 【问题描述】:

我必须更改UIPickerView 中所选行的颜色。

我确实设法改变了颜色,我知道这个问题已经有好几个回复了,无论如何这些都不能让我满意:通过这些实现,pickerview 的动画有问题,我不喜欢它。

这是当前解决方案的代码

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

    var label: UILabel
    if view == nil 
      label = UILabel()
     else if view is UILabel 
      label = view as! UILabel
     else 
      label = UILabel()
    

    let title: NSAttributedString
    if self.model?.selectedValue == row 
      title = UIPickerView.attributedString(with: .selectedRow, text: "\(model?.years[row] ?? 0)")
     else 
      title = UIPickerView.attributedString(with: .unselectedRow, text: "\(model?.years[row] ?? 0)")
    

    label.attributedText = title
    return label

当用户滚动时,我会重新加载组件。

但正如您在下图中看到的,当用户滚动时,绿色区域会移动,并且有几分之一秒的时间,绿色标签位于选择器指示器下方,而选定的行为黑色。

我想要的是选择器指示器内部的所有内容都是绿色的,而外部保持默认的灰色阴影。

我该如何实现?

【问题讨论】:

【参考方案1】:

您应该为此使用attributedTitleForRow,而无需使用NSAttributedString 创建您自己的标签。在您的didSelectRow 中,您重新加载所有组件,以便能够重置所有颜色并将新选择的颜色设置为绿色。

func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? 
    let color = (row == pickerView.selectedRow(inComponent: component)) ? UIColor.green : UIColor.black
    return NSAttributedString(string: self.model[row], attributes: [NSForegroundColorAttributeName: color])


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

【讨论】:

已经试过了,但是不行。我仍然可以看到年份变黑然后变绿。 不明白你的意思。滚动时要删除绿色吗? 不,我想要这样,当您向上或向下滚动时,选择视图中有一半字符串,另一个(未选择)中有一半字符串,它变成一半绿色和一半灰色。在默认实现中,没有颜色,当您滚动时,您可以看到标签半纯黑色和半黑色,alpha AttributeTitleForRow 有效。 self.model 引用您的数组,其中数据加载到选择器中。 为什么不只是pickerView.reloadComponent(component)?【参考方案2】:

你可以简单地使用这个代码

func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView 
    let label = view as? UILabel ?? UILabel()

    if pickerView.selectedRow(inComponent: component) == row 
        label.backgroundColor = UIColor.green
        label.frame = CGRect(x: 0, y: 0, width: 36, height: 36)
        label.layer.cornerRadius = 18
        label.layer.masksToBounds = true
        label.textColor = .white
        label.text = String(numbers[row])
        label.textAlignment = .center
    else 
        label.layer.cornerRadius = 25
        label.frame = CGRect(x: 0, y: 0, width: 36, height: 36)
        label.layer.cornerRadius = 18
        label.layer.masksToBounds = true
        label.textColor = .white
        label.text = String(numbers[row])
        label.textAlignment = .center
    
    return label


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

【讨论】:

你找到方法了吗?所选行一直持续到用户移开手指【参考方案3】:

效果更好,性能稍差。

extension ViewController: UIPickerViewDelegate

    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView 
        let info = BeatScalar.generalRates[row]
        let general = GeneralRateItem(info.cn, info.ita, info.val)
        // gray every row
        general.unselected()
        decorateView(pickerView, row: row)
        return general
    


    // Here is the logic 
    func decorateView(_ picker: UIPickerView, row: Int)

        var frame = picker.frame
        frame.origin.y = frame.origin.y + frame.size.height * 0.42
        frame.size.height = frame.size.height * 0.16

        let mini = max(row-1, 0)
        //   19 is total number
        let maxi = min(18, row+1)
        for i in mini...maxi
            if i != row, let item = picker.view(forRow: i, forComponent: 0) as? GeneralRateItem
                let f = item.convert(item.frame, to: picker)
                if frame.intersects(f) == false
                    // highlight the center row
                    item.selected()
                
            
        
    

    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat 
        return 44
    



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

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int        // 19 is total number
        return 19
    

【讨论】:

以上是关于在 UIPickerView 中更改选定行的颜色,滚动时也是如此的主要内容,如果未能解决你的问题,请参考以下文章

如何更改 UIPickerView 中选定行的颜色

在 Swift 中为选定的 UIPickerView 行设置动画背景颜色

如何在 iOS 14 中更改 UIPickerView 选择的色调颜色?

UIPickerView 选定行标签颜色

如何根据位置更改 UIPickerView 标题文本颜色

显示带有选定行的 UIPickerView