美文网首页IosiOS DeveloperiOS 开发
UIPickerView 由浅入深到精通

UIPickerView 由浅入深到精通

作者: 诸葛俊伟 | 来源:发表于2016-09-13 09:45 被阅读509次

本文将用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,行数返回数组的大小。需要注意的是,我们需要用到 UIPickerViewDelegateUIPickerViewDataSource,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 时,结果为

Demo1

当我们选中第二列的某一项时,结果为

Demo1

关于 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 时,结果为:

Demo2 CMU

当选中 Pitt 时,结果为:

Demo2 Pitt

自定义选择器视图

先献上一个坑,刚开始我用的一首完整的 .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!
    }

我们需要设置 rowHeightForComponentwidthForComponent 都为 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()
                })
            }

当你赢了之后,会显示赢了的图片:

win

最后还有个坑,现在还没填上,有会的大神教教我。pickerView 滚动的动画不会做。。但可以运行,可以判断 win or lose,就是动画效果没有出来。


References:

相关文章

网友评论

    本文标题:UIPickerView 由浅入深到精通

    本文链接:https://www.haomeiwen.com/subject/fvhmettx.html