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)