引言
最近公司有個(gè)需求, 要做一個(gè)PickerView
三級(jí)聯(lián)動(dòng)的菜單, 要求實(shí)現(xiàn)可供用戶選擇未來(lái)24小時(shí)內(nèi)任一時(shí)間點(diǎn)的功能, 最小可選單位為分. 其中第一列為今天或明天, 第二列為時(shí), 第三列為分. 由于前列時(shí)間點(diǎn)的選擇會(huì)影響后列時(shí)間點(diǎn)的可選范圍, 這就涉及到聯(lián)動(dòng)問(wèn)題. 網(wǎng)上的聯(lián)動(dòng)例子也有不少, 不過(guò)感覺(jué)大都是同一個(gè)例子, 都是說(shuō)的城市, 而且只是二級(jí)聯(lián)動(dòng)(雖然三級(jí)聯(lián)動(dòng)也大同小異), 畢竟這個(gè)案例自己也是花了點(diǎn)心思, 希望大家可以賞面來(lái)看看, 如果有剛好要做類(lèi)似功能的朋友, 也希望能對(duì)你們有所幫助. 這是最終效果圖:
正文
主要想就項(xiàng)目過(guò)程中遇到的幾個(gè)點(diǎn)說(shuō)一下自己的想法:
關(guān)于時(shí)間數(shù)據(jù)源
這個(gè)問(wèn)題確實(shí)有點(diǎn)惡心的, 第一列只要展示今天
和明天
, 沒(méi)問(wèn)題;
第二列問(wèn)題也不太大, 就2組數(shù)據(jù), 根據(jù)第一列的選擇情況分別展示即可, 不過(guò)這里需要注意的是, 如果當(dāng)前時(shí)間為59分時(shí), 當(dāng)前的時(shí)就不需要展示了, 因?yàn)榈谌幸呀?jīng)沒(méi)有可供用戶選擇的范圍了, 比如說(shuō)現(xiàn)在是10時(shí)59分, 那么10時(shí)就不需要顯示了, 應(yīng)該直接跳到11時(shí);
最?lèi)盒牡氖堑谌? 有3組數(shù)據(jù), 需要根據(jù)前2列的選中狀態(tài)來(lái)共同決定, 而且如果當(dāng)前時(shí)間為59分時(shí), 還要注意數(shù)據(jù)源的切換.-
關(guān)于聯(lián)動(dòng)的崩潰
這是個(gè)非常經(jīng)典的問(wèn)題, 網(wǎng)上很多地方也有說(shuō)到, 幾乎是做過(guò)聯(lián)動(dòng)的朋友都會(huì)遇到的, 就是在同時(shí)滑動(dòng)2列數(shù)據(jù)的時(shí)候很容易出現(xiàn)數(shù)組訪問(wèn)越界的情況, 然后就悲劇了. 具體的原因是在數(shù)據(jù)源方法- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;①
中使用了方法- (NSInteger)selectedRowInComponent:(NSInteger)component;②
去獲取當(dāng)前所選元素的下標(biāo), 然后去對(duì)應(yīng)的數(shù)組獲取第row
個(gè)下標(biāo)的元素, 乍看之下感覺(jué)沒(méi)什么問(wèn)題, 這也是為什么這么多人都會(huì)遇到這個(gè)崩潰情況的原因, 那究竟是哪里出問(wèn)題了呢? 原因在于代理方法- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;③
的調(diào)用時(shí)機(jī). 這個(gè)方法是在某列滾動(dòng)停止后就會(huì)調(diào)用, 我們一般會(huì)在此方法中刷新對(duì)應(yīng)其它列的數(shù)據(jù), 實(shí)現(xiàn)聯(lián)動(dòng)功能. 那么問(wèn)題來(lái)了, 上面的數(shù)據(jù)源方法①
調(diào)用的頻率是很高的, 在滑動(dòng)過(guò)程中會(huì)不停地調(diào)用, 沖突就來(lái)了.
舉個(gè)例子來(lái)說(shuō), 比如現(xiàn)在是20時(shí)30分, 當(dāng)?shù)谝涣羞x擇今天時(shí), 第二列的數(shù)據(jù)只有20-23這4個(gè)元素可選. 而當(dāng)?shù)谝涣羞x擇明天時(shí), 第二列的數(shù)據(jù)就多了, 有00-20共21個(gè)元素可選. 如果此時(shí)第一列選擇明天, 第二列正在往下標(biāo)大的元素方向滑動(dòng)進(jìn)行中, 然后第一列突然向今天滑去, 在滑到今天且準(zhǔn)備停止時(shí), 代理方法③
還未能調(diào)用, 也就是第二列的數(shù)據(jù)未能刷新數(shù)據(jù)源, 說(shuō)白了就是未能重新走一次數(shù)據(jù)源方法- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;④
來(lái)重新獲取需要展示的元素個(gè)數(shù), 也就是說(shuō)進(jìn)去數(shù)據(jù)源方法①
中的參數(shù)row
還可以達(dá)到20這個(gè)下標(biāo), 而此時(shí)在數(shù)據(jù)源方法①
中調(diào)用方法②
獲取第一列被選中的元素下標(biāo)時(shí)卻能獲取到被選中的是今天這個(gè)下標(biāo), 也就是說(shuō)會(huì)從20-23這4個(gè)元素中去拿值, 在只有4個(gè)元素的數(shù)組中取下標(biāo)為20的值, 就造成崩潰了.
可能說(shuō)得有點(diǎn)亂, 簡(jiǎn)單來(lái)說(shuō)就是方法②
是直接拿到當(dāng)前被選中的元素下標(biāo), 而代理方法③
則是在某一列停止?jié)L動(dòng)時(shí)才會(huì)被調(diào)用, 所以這里出現(xiàn)了數(shù)據(jù)沒(méi)匹對(duì)上的情況. 解決方法就是滿足調(diào)用次數(shù)少的一方, 也就是增加全局屬性來(lái)記錄當(dāng)前被選中的元素下標(biāo), 當(dāng)然是在代理方法③
中進(jìn)行更新賦值操作, 并且要在刷新其它列數(shù)據(jù)的命令之前(這里要補(bǔ)充一點(diǎn)的是, 由于是聯(lián)動(dòng)關(guān)系, 要注意做好遞規(guī)處理和下一列的選中下標(biāo)處理, 不然同樣會(huì)造成數(shù)組越界問(wèn)題), 取數(shù)據(jù)源時(shí)直接通過(guò)全局屬性替換掉原來(lái)的用方法②
獲取當(dāng)前被選中元素下標(biāo)的做法:
-
輸出所選結(jié)果
來(lái)到這里其實(shí)已經(jīng)沒(méi)有什么問(wèn)題了, 直接把整個(gè)結(jié)果輸出.
后記
其實(shí)在做這個(gè)案例時(shí)我還想到了其它奇奇怪怪的解決方案, 比如在數(shù)據(jù)源方法①
中刷新某一列的數(shù)據(jù), 或者在數(shù)據(jù)源方法①
中輸出結(jié)果等等, 不過(guò)這些做法都太影響性能了, 還有可能會(huì)產(chǎn)生其它BUG, 最后覺(jué)得還是目前這樣的處理方法比較可行, 也希望大家有好的想法可以一起分享指正.