多個(gè)TextInput情形下,鍵盤遮擋問題解決方案

Demo:MultiTextInputScrollView
最近一段時(shí)間做了一個(gè)基于React Native的APP PasswordAssistant ,其中需要用到表單,我選用了一個(gè)第三方組件 tcomb-form-native 。集成后,發(fā)現(xiàn)這個(gè)表單并沒有處理鍵盤遮擋問題,于是自己解決+搜索網(wǎng)友的方案,最終,借鑒 基于多個(gè)TextInput的鍵盤遮擋處理方案優(yōu)化 的實(shí)現(xiàn)方案,加上自己解決的幾個(gè)bug,實(shí)現(xiàn)了一個(gè)相對完美的解決方案。

解決鍵盤遮擋問題,是每一個(gè)開發(fā)者都會面臨的問題,在我們iOS開發(fā)中,通用的解決方案是監(jiān)聽鍵盤的高度,然后改變scrollView的偏移。在我所收集到的解決方案里,大部分也是基于這個(gè)思路來解決問題。由于多數(shù)方案僅考慮了單個(gè)TextInput的情況,因此,在多個(gè)TextInput情況下難免水土不服。盡管React Native官方提供了 keyboardavoidingview 用于解決鍵盤遮擋問題,但在實(shí)際應(yīng)用上也是有心無力。言歸正傳,放上我的Demo:MultiTextInputScrollView
接下來,我將結(jié)合demo中的代碼,情景再現(xiàn)下所遇到的問題及解決方案。
在demo中,一個(gè)相對重要的點(diǎn)是scrollView的一個(gè)屬性:onScrollEndDrag (function) ,這個(gè)屬性并未在官方文檔中提到,在源碼中才會發(fā)現(xiàn)這個(gè)屬性。參考網(wǎng)友的總結(jié):總結(jié)一下今年的react native 填坑之旅 。之所以說這個(gè)屬性重要,是因?yàn)橐鉀Q鍵盤遮擋就要適當(dāng)?shù)母淖僺crollView的偏移,獲取當(dāng)前的偏移位置再加上TextInput需要調(diào)整的偏移,才是最終調(diào)用scrollView的scrollTo方法需要傳的值。
剛開始根據(jù) 基于多個(gè)TextInput的鍵盤遮擋處理方案優(yōu)化 的方法,我也是用下面的代碼獲取scrollView的偏移:

onScrollEndDrag={(e) => {
          this.moveH = e.nativeEvent.contentOffset.y;
        }}

同樣也按照文中的方法做了異常數(shù)據(jù)處理,但存在一些明顯的bug,例如,TextInput經(jīng)常偏移過多或過少。盡管我在其基礎(chǔ)上增加了異常情形的處理,但還是存在微小的bug。今天抽出時(shí)間深入分析了一下bug的原因,發(fā)現(xiàn)是獲取scrollView的偏移的方法不對。正確的取值代碼如下:

onScrollEndDrag={(e) => {
          this.moveH = e.nativeEvent.targetContentOffset.y;
          this.moveHeight = 0;
          console.log('nativeEvent==' + JSON.stringify(e.nativeEvent));
        }}

在scrollView的y偏移為0的情況下,往下拖拽scrollView,待松手后,控制臺打印的log如下:

nativeEvent=={
 "layoutMeasurement":{"width":375,"height":667},
 "targetContentOffset":{"x":0,"y":0},
 "contentOffset":{"x":0,"y":-29},
 "contentInset":{"right":0,"top":0,"left":0,"bottom":0},
 "velocity":{"x":0,"y":-2.8134224302815314},
 "zoomScale":1,
 "contentSize":{"width":375,"height":720}
}

可以看出,contentOffset.y 為負(fù)值 而 targetContentOffset.y 為零。contentOffset.y 之所以為負(fù)值是因?yàn)閟crollView的彈簧效果決定了scrollView可以拖拽超出它的contentSize,上面說的異常情形處理就是為了填這個(gè)坑。而我們需要的是scrollView實(shí)際偏移的值,而非包含超出contentSize的值。因此將contentOffset.y 換成 targetContentOffset.y 就完美了,也不存在異常數(shù)據(jù)情形了。
是否真的完美了呢?可以說是的,如果你不在乎細(xì)小瑕疵的話。這里說的細(xì)小瑕疵指的是,存在一種情形,即:點(diǎn)擊非TextInput以外的空白區(qū)域,退下鍵盤,會發(fā)現(xiàn)scrollView并沒有恢復(fù)到原來的位置,具體復(fù)現(xiàn)方法是:選中一個(gè)TextInput A,由于鍵盤遮擋,TextInput向上滾動一段距離,此時(shí)再點(diǎn)擊A的上面一個(gè)TextInput B。由于B此時(shí)距屏幕底部的距離大于(鍵盤的高度+行高),所以,代碼中的this
.needMove為false,導(dǎo)致,鍵盤退下時(shí),scrollView并沒有恢復(fù)到原來的位置。 解決方法有兩種:

  1. 將scrollView的keyboardShouldPersistTaps屬性由'handled'改成'always',這樣,點(diǎn)擊非TextInput以外的空白區(qū)域,鍵盤就不會退下了。
  2. 記錄鍵盤彈起后TextInput調(diào)整的偏移量this.moveHeight,在鍵盤消失時(shí)判斷如果this.moveHeight大于零,就讓scrollView恢復(fù)到原來的位置。另外,在onScrollEndDrag方法里記得重置this.moveHeight = 0 。

到這里,終于算是真正的完美了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容