課題
メモ帳アプリで、1ページ丸々TextInputのページがあって、
そこでscrollするとフォーカスもあたってしまうという問題
やり方1 TextInputを別要素で囲って制御する
参考:https://www.366service.com/jp/qa/cabd3c41d1468c813e5c2447e6911a01
ScrollView > TouchableHighlight > TextINput
でtextInputはpointerEvents=noneにしておく(pointerEventsはstateにset)。TextInputではイベントを拾わずScrollは外側のscrollViewで実現する。
PressはTouchableHighlightで拾い、そこでInputTextのpointerEventsをautoに変更し、ref経由でfocusを実行する。という感じ。
scrollとフォーカスの制御は問題なかったが、以下の点で問題があった。
-
フォーカスした時のkeybord避けにkeybord-spacerを使っていたのだが、高さが微妙にキーボードに被ってしまう。
-
なので..自分でキーボード表示時のにScrollViewの高さを変更して調整しようと考えたが...
-
ScrollViewや内部の要素にheightを指定するとスクロール出来ない。=>外をViewで囲って高さを指定することでクリア。
-
TouchableHighligh、TextInputに高さが指定できないので、新規作成のときは1行だけの高さしかなくてタッチ出来る範囲が狭くなる。不便。
-
キーボーどと被らないかもしれないが、Viewの一番下がキーボードの直上にくる。選択した位置がキーボードの上に来るわけではないので、これでは駄目.... 更にScrollToなどでの調整が必要
-
高さをDimensions.get('window').heightから取得するとはみ出してしまう..navigationヘッダーやSafeareaを考慮しないと行けない。ちょっと小さめにしておくことが必要か.
-
そもそもフォーカスをした時に、タッチした位置が選択状態にならない、もう1回タッチしないと行けない。これがでかい。
ちょっと、課題が多すぎる..まだまだ時間がかかってしまう..
一応参考ソース
<ScrollView>
<TouchableOpacity
onPress={(e) => this.onpressWrapper(e)}
>
<TextInput
style={[styles.textInput ]}//高さを指定するとscrollがそこで止まる
multiline={true}
onChangeText={(text) => this.save(text)}
onFocus={() => console.log("focus")}
defaultValue={this.state.text}
pointerEvents={this.state.pointerEvents}
ref={this.textInputRef}
/>
</TouchableOpacity>
</ScrollView>
this.textInputRef = React.createRef();
onpressWrapper = (e) => {
const ref = this.textInputRef.current
this.setState({
pointerEvents: 'auto',
}, ref.focus())
}
やり方2 InputScrollView
以下使ってみる..
https://github.com/baijunjie/react-native-input-scroll-view
ここもスクロール制御は良いけど、フォーカスとキーボード回避がネック。
KeybordAvoidanceViewを使っており、1行程度がキーボードと被ってしまう。。。
フォーカス時にも上手くタッチ位置がキーボード上に来ない。
結論
スクロールでフォーカスが当たるほうがまだ使いやすい...
色々なテクニカルなことは他にもありそうだけど、もうちょっと余裕のある時に検討。
あとはネイティブで作ったらどういう挙動なんだろうか、確認したい所。
やり方3
InputScrollViewの中を見て、以下のコードをパクったら確かにscroll時にfocusが当たらなくなった。responderを使うと。
しかし一報で、keybordSpacerが効かなくなってしまった。(キーボードに被ってスクロールしていってしまう....)
もう少しで出来そうなのに...
これ以上はどんどん、トリッキーになって行きそうだし、、
規模も小さいアプリ出し、ネイティブアプリに変更しようかな。
onFocus = event => {
const target = event.target;
this._curFocus = target;
}
onBlur = event => {
const target = event.target;
if (this._curFocus === target) this._curFocus = null;
}
_checkhandleGesture = event => {
const target = event.target;
if (target === this._curFocus) return false;
const targetInst = event._targetInst;
const uiViewClassName = targetInst.type || // >= react-native 0.49
targetInst.viewConfig.uiViewClassName; // <= react-native 0.48
return uiViewClassName === 'RCTTextField' || uiViewClassName === 'RCTTextView' || uiViewClassName === 'RCTMultilineTextInputView';
};
renderScrollTextInput = () => {
return (
<ScrollView
onFocus={this.onFocus}
onBlur={this.onBlur}
style={{ flex: 1 }}
>
<View style={{ flex: 1 }} onStartShouldSetResponderCapture={isIOS ? this._checkhandleGesture : null}>
{this._renderInputText()}
</View>
</ScrollView>
)
}