返回 导航

Swift

hangge.com

Swift - UIPickerView无限循环滚动的实现(数据循环显示)

作者:hangge | 2017-12-11 08:10
UIPickerView 数据选择器在平时开发中经常会用到。当里面的数据项很多时,为了有更好的用户体验,许多应用的做法都是让它能循环滚动。也就是 pickerView 选项不管往上,还是往下都不会滚到底,数据会一直循环显示。下面演示如何实现这个功能。

一、单列 PickerView 的无限滚动

1,效果图

我们在 pickerView 里显示一月到十二月的数据。同时不管如何滚动,数据项总是会循环显示。
         

2,实现原理

(1)虽然 pickerView 没有自带这种循环滚动的功能,但我们可以把行数设成一个很大的值(比如本样例就设成 12 万)。然后让其在初始化时默认选择中间位置的项目。这样用户不管向上,还是向下基本上不能一次滚到底。
(2)同时我们在滚动停止后,还会自动将当前选中行移回到中间部分(当然动画要关闭,让用户察觉不到其实我们改变了选中行)。这样就会让用户保持无限滚动的幻觉。

3,样例代码

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    //选择框
    var pickerView: UIPickerView!
    
    //pickerView里的数据行数(12万)
    let pickerDataSize = 120_000
    
    //pickerView显示的数据
    let pickerData = ["一月", "二月", "三月", "四月", "五月", "六月",
                      "七月", "八月", "九月", "十月", "十一月", "十二月"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //初始化pickerView
        setupPickerView()
    }
    
    //初始化pickerView
    func setupPickerView() {
        pickerView = UIPickerView()
        pickerView.dataSource = self
        pickerView.delegate = self
        
        pickerView.frame = CGRect(x:0, y:64, width:view.frame.width, height:300)
        view.addSubview(pickerView)
        
        //让pickerView默认选中中间项
        pickerView.selectRow(pickerDataSize/2, inComponent: 0, animated: false)
    }
    
    //设置pickerView的列数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    //设置pickerView的行数
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int)
        -> Int {
            return pickerDataSize
    }
    
    //设置pickerView各选项的内容
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int,
                    forComponent component: Int) -> String? {
        return pickerData[row % pickerData.count]
    }
    
    //pickerView选中某一项后会触发
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int,
                    inComponent component: Int) {
        //重新让pickerView又选回中间部分的数据项
        let position = pickerDataSize / 2 + row % pickerData.count
        pickerView.selectRow(position, inComponent: 0, animated: false)
    }
}

二、多列 PickerView 的无限滚动

1,效果图

  • 我们这里使用 pickerView 实现一个省市联动选择器。第一列选择一个省后,第二列会显示该省下的所有市。
  • 同时不管是第一列还是第二列,数据项都是可以循环滚动的。
           

2,实现原理

(1)这个实现原理同上面是一样的,同样是把每列的行数设置成一个特别大的值,然后初始时让其处于中间位置。
(2)由于有多列,每列的原始数据量是不同的。所以这里设定一个倍数(1 万),每列的行数都是:原始数据行数 * 倍数。

3,样例代码

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    //选择框
    var pickerView: UIPickerView!
    
    //pickerView里的原始数据的倍数(1万)
    let dataSizeMultiple  = 10_000
    
    //pickerView显示的数据
    let provinceData = ["浙江", "江苏", "广东", "湖北"]
    let cityData = [["杭州", "丽水", "宁波", "舟山", "衡州", "嘉兴"],
                       ["南京", "苏州", "无锡", "常州", "泰州"],
                       ["广州", "珠海", "惠州", "清远"],
                       ["武汉", "孝感", "恩施", "黄石", "荆州"],]
    
    //选择的省索引
    var provinceIndex = 0
    //选择的市索引
    var cityIndex = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //初始化pickerView
        setupPickerView()
    }
    
    //初始化pickerView
    func setupPickerView() {
        pickerView = UIPickerView()
        pickerView.dataSource = self
        pickerView.delegate = self
        
        pickerView.frame = CGRect(x:0, y:64, width:view.frame.width, height:200)
        view.addSubview(pickerView)
        
        //让pickerView默认选中中间项
        pickerView.selectRow(dataSizeMultiple * provinceData.count/2, inComponent: 0,
                             animated: false)
        pickerView.selectRow(dataSizeMultiple * cityData[0].count/2, inComponent: 1,
                             animated: false)
    }
    
    //设置pickerView的列数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }
    
    //设置pickerView的行数
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int)
        -> Int {
            if component == 0 {
                return dataSizeMultiple * provinceData.count
            }else{
                return dataSizeMultiple * cityData[component % provinceData.count].count
            }
    }
    
    //设置pickerView各选项的内容
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int,
                    forComponent component: Int) -> String? {
        if component == 0 {
            return provinceData[row % provinceData.count]
        }else{
            return cityData[provinceIndex][row % cityData[provinceIndex].count]
        }
    }
    
    //pickerView选中某一项后会触发
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int,
                    inComponent component: Int) {
        //重新让pickerView又选回中间部分的数据项
        if component == 0 {
            provinceIndex = row % provinceData.count
            let position = dataSizeMultiple * provinceData.count/2 + provinceIndex
            pickerView.selectRow(position, inComponent: 0, animated: false)
            //省改变后同步改变对应的市数据
            cityIndex = 0
            pickerView.reloadComponent(1)
            pickerView.selectRow(dataSizeMultiple * cityData[provinceIndex].count/2,
                                 inComponent: 1, animated: false)
        }else{
            cityIndex = row % cityData[provinceIndex].count
            let position = dataSizeMultiple * cityData[provinceIndex].count/2 + cityIndex
            pickerView.selectRow(position, inComponent: 1, animated: false)
        }
    }
    
    //设置行高
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int)
        -> CGFloat {
        return 36
    }
}
评论

全部评论(0)

回到顶部