swift下使UIPickerView数据关联

起因

今天给室友做ios作业时遇到的问题。
之前在慕课网看了些视频,swift基本语法和实战,大致了解了,敲了几百行代码热手。
我想按照视频看到的,利用Picker View实现一个简单的看图片的程序,再用上Navigation Controllor,考虑到室友喜欢曼城队,图片主角就选曼城的球员了,本来想用上Tab Bar Controllor,分为前中后场和门将四个分类显示球员。用到Picker View控件的时候想到了可以在Picker View里显示两个数据列,左边显示前中后场分类,右边显示对应的球员。
于是就遇到了问题:左边的选项改变,右边的选项不会更新。

下面的图是最终我想要得到的效果

探索

我首先想到的是修改下面这个接口,第一个函数的返回值表示Picker View控件有几列,用component表示,因为有两列,所以返回2。第二个函数返回每一列有几项,第一列的数目是固定的,问题在第二列的个数,需要根据第一列的值来确定,我一开始想根据不同的行row值来确定选了第一列的哪项,但是第二个函数里没有row这个参数。(下面两段代码都是最终的正确代码)

extension ViewController: UIPickerViewDataSource {
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 2
    }
    
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if component == 0 {
            return player_location.count
        }   else {
            return show_players.count
        }
    }
}

我之所以想到用row值,是因为第二个接口里有一个不能省略的函数有row这个参数

extension ViewController: UIPickerViewDelegate {
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        if component == 0 {
            return player_location[row]
        }   else {
            /*if player.selectedRowInComponent(0) == 0 {
                return fw_players[row]
            }   else if player.selectedRowInComponent(0) == 1 {
                return mf_players[row]
            }   else {
                return wb_players[row]
            }*/
            return show_players[row]
        }
    }
}

其中第一个pickerView函数是不能省略的,似乎是在OC里就有的接口。返回了每一列要绑定的数据。在这里的row是当前这一列选中的行,从0开始,所以在数组中可以直接作为下标。我就是想根据第一列的row值来改变第二列显示的内容。既然没有,我就尝试把UIPickerViewDataSource接口pickerView函数第二列的返回值设为一个很大的值,大于所有分类下的数组大小。可惜不行,返回的个数必须与数组的大小严格一致,否则运行会崩溃。

我去除了门将分类,把剩下三个分类下数组的大小都调整为5,这样UIPickerViewDataSource接口的pickerView函数关于第二列的返回值就可以固定为5,解决了返回值问题。然后在UIPickerViewDelegate接口的pickView函数里根据第一列的值返回不同的数组(前中后场三个数组)。但运行时,改变了第一列的值,第二列还是不变,显然这个方法不是在值改变时调用的。

上面代码中的player就是这个Picker View实例化的对象。我在它的方法中翻了翻,发现了selectedRowInComponent()这个函数,参数传第几列,它返回这一列的第几行被选中。我猜测它应该和row参数的效果一样,我改成如上面代码注释部分所写,运行发现它和row参数的效果不一样,并且它真的使第二列的内容发生了改变!但是这个改变很隐晦,需要把第二列滑动,然后因滑动而看不见的部分才会改变,并且不管这个分类下数组的大小是几,最终都显示和第一个分类的数组大小一样数目的内容,因为初始时第一列的第一个值被选中。此时我的第一个分类数组大小是4,第二个和第三个大小都是6,我觉得如果第二个或第三个大小比第一个小的话运行也会崩溃,不过我没尝试。显然,这不是解决方案。

解决

先百度答案,发现全是OC的方法,没有swift的方法,再google,搜索结果和百度差不多。本来想在stackoverflow上面提问,觉得编英语我会十分痛苦,就在segmentfault上提问了,然后继续看OC的解决方法希望找到灵感,虽然语法不同,但都继承了两个相同的接口,并且OC里多了几个函数。我试了试,发现UIPickerViewDelegate这个接口里pickerView函数有一个重载和OC里的那个参数一样,试着写了一下,因为不可省的pickView里不能按照先前的方法return,所以又定义了一个显示数组,在重载的pickView里用来更新这个数组。最后调用reloadComponent(1)方法,表示刷新Picker View控件的第二列,运行发现成功了。重载函数代码如下,这个函数才是Picker View控件选项值改变时调用的函数

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if component == 0 {
            if player.selectedRowInComponent(0) == 0 {
                show_players = fw_players
            }   else if player.selectedRowInComponent(0) == 1 {
                show_players = mf_players
            }   else if player.selectedRowInComponent(0) == 2 {
                show_players = wb_players
            }   else {
                show_players = gk_players
            }
            player.reloadComponent(1)
        }
    }
文 / altair21
4 COMMENTS
  1. 2015/09/17
    inzaghi

    这是曼城的球迷啊

  2. 2015/11/24
    jinjin

    楼主这个blog咋搭建的。写一个搭建这个blog的文章吧。。。

    • 2015/11/24
      altair21
      @jinjin vps搭建wordpress博客,网上教程很多,搜一下吧
LEAVE A REPLY

loading