本文将用3个 Demo,由浅入深的学习并实践 UIPickerView。具体代码可以参考我的 Github,还有我的博客。本文目录:
- UIPickerView 常用的属性和方法
- 单列选择器
- 多列选择器(Demo1)
- 相互依赖的多列选择器(Demo2)
- 自定义选择器视图(Demo3, 老虎机实例)
UIPickerView 常用的属性和方法:
- 这两个是 Required,必须都要实现,少了任何一个都会报错,
does not confirm to protocol
,详情请见 SO: - numberOfComponentsInPickerView (只读属性):获取列数。
- pickerView:numberOfRowsInComponent: 在某一列中的行数。
- 其它方法,这里只举其中几个为例。
- pickerView:widthForComponent: 返回 CGFloat 值作为指定列的宽度。
- pickerView:didSelectRow:inComponent: 当用户选择了某一列的某一行时触发。这里需要注意的是用户需要做出 选择 才可以。比如当用户第一列本来就要选第一个,他就直接跳过第一列,选择了第二列的某一行,那么只能捕捉到第二列的选择信息。
- pickerView:titleForRow:forComponent: 返回一个 String 值作为指定列的列表项的标题。
单列选择器
只需要在 storyboard 中拉一个 Picker View,在 ViewController 中实现最基本的两个方法就可以,列数返回 1,行数返回数组的大小。需要注意的是,我们需要用到 UIPickerViewDelegate
和 UIPickerViewDataSource
,UIPickerView的事件处理由其委托对象完成。
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource { }
然后在 viewDidLoad() 中加上
self.pickerView.delegate = self
self.pickerView.dataSource = self
第一次用 UIPickerViewDelegate 和 UIPickerViewDataSource 的人应该会很不习惯,因为我就是刚学的。。。但掌握了之后就觉得用处好大,很多东西都可以做的和以前不同了,比如 tableView 也可以做的更灵活了,改天再写一篇 tableView 的文章,从基础开始一步一个脚印学习。
多列选择器
这里我用2列的选择器,使用一个二维数组存储我们的数据
var campus = [String]()
var address = [String]()
var pickerData = [[String]]()
某列的行数为
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData[component].count
}
某行某列的标题
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return campus[row]
} else {
return address[row]
}
}
这里我用了一个 alert 来提示我们选中的是哪一列哪一行。
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let cam = component == 0 ? campus : address // 第一列为学校,第二列为具体地址
let add = component == 0 ? "campus" : "address"
let alert = UIAlertController(title: "Your Address is", message: "Your \(add) is \(cam[row])", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: {action in
switch action.style {
case .Default:
print("default")
case .Cancel:
print("cancel")
case .Destructive:
print("destructive")
}
}))
self.presentViewController(alert, animated: true, completion: nil)
}
当我们选中第一列的 CMU 时,结果为
![](https://img.haomeiwen.com/i1960892/59bc00bb62fd8c3d.png)
当我们选中第二列的某一项时,结果为
![](https://img.haomeiwen.com/i1960892/4e5cf59a7bf07200.png)
关于 swift 中的 alert,新手可以看看这个 OS 的回答,应该会有帮助。
相互依赖的多列选择器
上面所说的多列选择器,各列之间没有关系,但很多时候我们需要后一列根据前一列做出变动。比如我的 Demo 中,第一列选择 CMU 时,第二列需要为空或者就只有 CMU 一个选项,但选择 Pitt 的时候,需要出现那些更多的具体选项。
为了实现这个相互依赖的功能,Dictionary
是关键。keys
就是 campus,values
就是相对应的地址。我们用一个数组存 keys
,用一个字典存 keys:[values]
的配对。再用一个 selectedCamp 来标记选中的 campus。代码如下:
申明
var campus = [String]()
var address = [String:[String]]()
var selectedCamp: String!
viewDidLoad 中:
campus = ["CMU", "Pitt"]
address = dictionaryWithValuesForKeys(campus) as! [String:[String]]
// 设置默认选中campus中的第一个元素
selectedCamp = campus[0]
实现这个 dictionaryWithValuesForKeys() 方法:
override func dictionaryWithValuesForKeys(keys: [String]) -> [String : AnyObject] {
return ["CMU": ["CMU"], "Pitt": ["Hillman Library", "Eberly Hall", "Benedum Hall", "Salk Hall", "Information Science Building", "Terrace ST & Lothrop ST", "700 Technology Dr.", "pick up at store"]]
}
后面的那些方法依次为:返回2列,行数根据我们第一列的选择进行计算, address[selectedCamp]!.count
,相应的 title
也是相对于第一列的选择计算得出:address[selectedCamp]![row]
。
这个 Demo 我也用 alert 来显示选中的地址。
if component == 0 {
selectedCamp = campus[row]
// 根据选中的校园加载第二个列表
self.pickerView.reloadComponent(1)
} else {
let camp = component == 0 ? campus : address[selectedCamp]!
let tip = component == 0 ? "campus" : "address"
// 使用一个UIAlertView来显示用户选中的列表项
let alert = UIAlertController(title: "Your address is", message: "Your \(tip) is \(camp[row])", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
当选中 CMU 时,结果为:
![](https://img.haomeiwen.com/i1960892/b157fe71cc086d69.png)
当选中 Pitt 时,结果为:
![](https://img.haomeiwen.com/i1960892/a16395481a590870.png)
自定义选择器视图
先献上一个坑,刚开始我用的一首完整的 .wav 格式的歌曲来做背景音乐,但是不管怎么改动代码,都会收到 WARNING: 998
的错误提示。最后我把音乐换成10秒左右的 .wav 音效才算可以。折腾了好久,第一次在 ios 中使用音效。其实不难,代码如下:
// 版本很多,但这样的形式我最喜欢,因为最方便,只要在开头 import AudioToolbox 即可
// 在代码中任何一个地方都可以直接以 SystemSoundID.playFileNamed("play") 来播放音效
extension SystemSoundID {
static func playFileNamed(fileName: String, withExtenstion fileExtension: String? = "wav") {
var sound: SystemSoundID = 0
if let soundURL = NSBundle.mainBundle().URLForResource(fileName, withExtension: fileExtension) {
AudioServicesCreateSystemSoundID(soundURL, &sound)
AudioServicesPlaySystemSound(sound)
}
}
}
其实所谓自定义,就是把本来是 String 的列表内容,换成了 UIImageView,用一个数组存储我们要用到的图片,然后在 PickerView 中加载 UIImageView。这里用到了 viewForRow
返回 UIView
那个方法。代码如下:
func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
var view = view
// 如果可重用的view的tag不等于kImageTag,表明该view已经不存在,需要重新创建
if view?.tag == nil || view?.tag != kImageTag {
view = UIImageView(image: images[row])
view?.tag = kImageTag
view?.userInteractionEnabled = false
}
return view!
}
我们需要设置 rowHeightForComponent
和 widthForComponent
都为 60 就行,最好别太大,装不下。。我的图片也是做过的,做成了 60*60 的。
再实现点击事件,即开始按钮。点击之后依次为,播放背景音效,然后计算图片出现次数,如果出现次数大于2,则算赢,否则算输。代码如下:
// 在 result 中为该随机数记录次数
if (result[selectedValue] != nil) {
let newCount = result[selectedValue]
result[selectedValue] = newCount! + 1
} else {
result[selectedValue] = 1
}
// 随机数的最大出现次数
var maxOccur = 1
for n in result.keys {
// 只要任何随机数的出现次数大于 maxOccur
if result[n] > maxOccur {
maxOccur = result[n]!
}
}
if maxOccur >= 2 {
// 如果赢了,延迟 0.5 秒执行 showWin() 方法
delay(0.5, closure: {
self.showWin()
})
} else {
// 如果输了,延迟 0.5 秒执行 showLose() 方法
delay(0.5, closure: {
self.showLose()
})
}
当你赢了之后,会显示赢了的图片:
![](https://img.haomeiwen.com/i1960892/26935c257f241422.png)
最后还有个坑,现在还没填上,有会的大神教教我。pickerView 滚动的动画不会做。。但可以运行,可以判断 win or lose,就是动画效果没有出来。
References:
- UIPickerView 基础, iOS9 UIPickerView Example and Tutorial in Swift and Objective-C
- UIPickerView 提高篇(oc),疯狂ios讲义之选择器(UIPickerView)
网友评论