TextInputにフォーカスがあるときに、キーボードの後ろからウィンドウを自動スライドさせる方法は?


90

ネイティブアプリがウィンドウを自動スクロールするこのハックを見てきましたが、React Nativeでそれを行うには最善の方法を考えています... <TextInput>フィールドがフォーカスを取得してビューの低い位置に配置されると、キーボードがテキストフィールドを覆います。

この問題は、UIExplorerのTextInputExample.jsビューの例で確認できます。

誰かが良い解決策を持っていますか?


3
これはGithubトラッカーの問題として追加し、何かが発生するかどうかを確認することをお勧めします。これは非常に一般的な不満になるためです。
Colin Ramsay

回答:


83

2017年の回答

KeyboardAvoidingViewおそらく今行くための最良の方法です。こちらのドキュメントをご覧くださいKeyboardアニメーションを実行するためのより多くの制御を開発者に提供するモジュールと比較して、それは本当に簡単です。Spencer Carliは、彼のメディアブログですべての可能な方法を示しまし

2015年の回答

でこれを行う正しい方法react-nativeは、外部ライブラリを必要とせず、ネイティブコードを利用し、アニメーションを含みます。

最初にonFocus、それぞれTextInput(またはスクロールしたい他のコンポーネント)のイベントを処理する関数を定義します。

// Scroll a component into view. Just pass the component ref string.
inputFocused (refName) {
  setTimeout(() => {
    let scrollResponder = this.refs.scrollView.getScrollResponder();
    scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
      React.findNodeHandle(this.refs[refName]),
      110, //additionalOffset
      true
    );
  }, 50);
}

次に、レンダリング関数で:

render () {
  return (
    <ScrollView ref='scrollView'>
        <TextInput ref='username' 
                   onFocus={this.inputFocused.bind(this, 'username')}
    </ScrollView>
  )
}

これは、RCTDeviceEventEmitterキーボードイベントとサイズ変更にを使用しRCTUIManager.measureLayout、を使用してコンポーネントの位置を測定し、で必要な正確なスクロール移動を計算しscrollResponderInputMeasureAndScrollToKeyboardます。

あなたはで遊んすることをお勧めしますadditionalOffset、あなたの特定のUIデザインのニーズに合わせて、パラメータ。


5
これは良い検索ですが、私にとっては十分ではありませんでした。ScrollViewはTextInputが画面上にあることを確認しますが、ScrollViewはユーザーがスクロールできないキーボードの下のコンテンツをまだ表示していました。ScrollViewプロパティ "keyboardDismissMode = on-drag"を設定すると、ユーザーはキーボードを閉じることができますが、キーボードの下に十分なスクロールコンテンツがない場合、エクスペリエンスは少し不快になります。そもそもキーボードが原因でScrollViewだけがスクロールする必要がある場合にバウンスを無効にすると、キーボードを
閉じて

2
@ miracle2k-入力がぼやけたとき、つまりキーボードが閉じたときに、スクロールビューの位置をリセットする関数があります。多分これはあなたの場合に役立つかもしれませんか?
シャーロック

2
@Sherlockぼかしスクロールビューのリセット関数はどのようなものですか?ところで素晴らしいソリューション:)
ライアン・マクダーモット

8
新しいReact Nativeバージョンでは、次を呼び出す必要があります。* 'react-native'からReactNativeをインポートします。*呼び出す前* ReactNative.findNodeHandle()*そうしないとアプリがクラッシュします
amirfl

6
現在、import {findNodeHandle} from 'react-native' stackoverflow.com
questions / 37626851 /

26

Facebook は、この問題を解決するためにreact 0.29でオープンソース化 KeyboardAvoidingViewしています。ドキュメントと使用例はここにあります


32
KeyboardAvoidingViewに注意してください、それは簡単に使用できません。期待どおりに動作するとは限りません。ドキュメントは実質的に存在しません。
Renatoが2016

ドキュメントと動作が改善されました
antoine129 '29

私が抱えている問題は、KeyboardAvoidingViewが私のiPhone 6シミュレータでキーボードの高さを65と測定するため、私のビューがキーボードの後ろに隠れていることです。
Marc

私がそれを管理できる唯一の方法は、トリガーされたボトムパディングアプローチを通じてでしたDeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
Marc

12

コード形式のいくつかのreact-native-keyboard-spacerと@Sherlockのコードを組み合わせて、TextInput要素を持つ任意のビューをラップできるKeyboardHandlerコンポーネントを作成しました。魅力的な作品!:-)

/**
 * Handle resizing enclosed View and scrolling to input
 * Usage:
 *    <KeyboardHandler ref='kh' offset={50}>
 *      <View>
 *        ...
 *        <TextInput ref='username'
 *          onFocus={()=>this.refs.kh.inputFocused(this,'username')}/>
 *        ...
 *      </View>
 *    </KeyboardHandler>
 * 
 *  offset is optional and defaults to 34
 *  Any other specified props will be passed on to ScrollView
 */
'use strict';

var React=require('react-native');
var {
  ScrollView,
  View,
  DeviceEventEmitter,
}=React;


var myprops={ 
  offset:34,
}
var KeyboardHandler=React.createClass({
  propTypes:{
    offset: React.PropTypes.number,
  },
  getDefaultProps(){
    return myprops;
  },
  getInitialState(){
    DeviceEventEmitter.addListener('keyboardDidShow',(frames)=>{
      if (!frames.endCoordinates) return;
      this.setState({keyboardSpace: frames.endCoordinates.height});
    });
    DeviceEventEmitter.addListener('keyboardWillHide',(frames)=>{
      this.setState({keyboardSpace:0});
    });

    this.scrollviewProps={
      automaticallyAdjustContentInsets:true,
      scrollEventThrottle:200,
    };
    // pass on any props we don't own to ScrollView
    Object.keys(this.props).filter((n)=>{return n!='children'})
    .forEach((e)=>{if(!myprops[e])this.scrollviewProps[e]=this.props[e]});

    return {
      keyboardSpace:0,
    };
  },
  render(){
    return (
      <ScrollView ref='scrollView' {...this.scrollviewProps}>
        {this.props.children}
        <View style={{height:this.state.keyboardSpace}}></View>
      </ScrollView>
    );
  },
  inputFocused(_this,refName){
    setTimeout(()=>{
      let scrollResponder=this.refs.scrollView.getScrollResponder();
      scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
        React.findNodeHandle(_this.refs[refName]),
        this.props.offset, //additionalOffset
        true
      );
    }, 50);
  }
}) // KeyboardHandler

module.exports=KeyboardHandler;

iOSシミュレーターにキーボードが表示されないようにする簡単で明白なものはありますか?
seigel 2015年

1
Command + K(ハードウェア->キーボード->トグルソフトウェアKeboard)を試しましたか?
ジョンケンダル2015年

これの修正バージョンをここで試してください:gist.github.com/dbasedow/f5713763802e27fbde3fc57a600adcd3これは、壊れやすいimoだと思うタイムアウトに依存しないため、これがより良いと思います。
CoderDave

10

まず、react-native-keyboardeventsをインストールする必要があります。

  1. XCodeのプロジェクトナビゲーターで、[ライブラリ]を右クリックし、[[プロジェクトの名前]にファイルを追加]をクリックします。node_modulesに移動します。➜react-native-keyboardeventsに移動し、.xcodeprojファイルを追加します。
  2. XCodeのプロジェクトナビゲーターで、プロジェクトを選択します。keyboard * eventsプロジェクトのlib * .aをプロジェクトのビルドフェーズに追加します。Binバイナリをライブラリにリンクする以前にプロジェクトナビゲーターで追加した.xcodeprojファイルをクリックし、[ビルド設定]タブに移動します。(「基本」の代わりに)「すべて」がオンになっていることを確認します。ヘッダー検索パスを探し、$(SRCROOT)/../ react-native / Reactと$(SRCROOT)/../../ Reactの両方が含まれていることを確認します-両方を再帰的としてマークします。
  3. プロジェクトを実行する(Cmd + R)

次に、JavaScriptランドに戻ります。

react-native-keyboardeventsをインポートする必要があります。

var KeyboardEvents = require('react-native-keyboardevents');
var KeyboardEventEmitter = KeyboardEvents.Emitter;

次に、ビューでキーボードスペースの状態を追加し、キーボードイベントをリッスンして更新します。

  getInitialState: function() {
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, (frames) => {
      this.setState({keyboardSpace: frames.end.height});
    });
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, (frames) => {
      this.setState({keyboardSpace: 0});
    });

    return {
      keyboardSpace: 0,
    };
  },

最後に、すべての下にあるレンダー関数にスペーサーを追加して、サイズが大きくなるとアイテムを押し上げます。

<View style={{height: this.state.keyboardSpace}}></View>

アニメーションAPIを使用することもできますが、簡単にするために、アニメーションの後で調整します。


1
アニメーションの実行方法に関するコード例やその他の情報を見るのは素晴らしいことです。ジャンプはかなりぎくしゃくしていて、「will show」と「did show」のメソッドのみで作業していると、キーボードアニメーションがどのくらい長くなるか、または「will show」からどのくらいの高さになるかを推測する方法がわかりません。
スティーブン

2
react-native@0.11.0-rcは、DeviceEventEmitterを介してキーボードイベント(たとえば、「keyboardWillShow」)を送信するため、これらのイベントのリスナーを登録できます。ただし、ListViewを処理するときは、ListViewのスクロールビューでscrollTo()を呼び出すほうが効果的でthis.listView.getScrollResponder().scrollTo(rowID * rowHeight); あることがわかりました。これは、onFocusイベントを受け取ると、行のTextInputで呼び出されます。
Jed Lau

4
RCTDeviceEventEmitterが機能するため、この回答は無効になりました。
シャーロック


6

これを試して:

import React, {
  DeviceEventEmitter,
  Dimensions
} from 'react-native';

...

getInitialState: function() {
  return {
    visibleHeight: Dimensions.get('window').height
  }
},

...

componentDidMount: function() {
  let self = this;

  DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
    self.keyboardWillShow(e);
  });

  DeviceEventEmitter.addListener('keyboardWillHide', function(e: Event) {
      self.keyboardWillHide(e);
  });
}

...

keyboardWillShow (e) {
  let newSize = Dimensions.get('window').height - e.endCoordinates.height;
  this.setState({visibleHeight: newSize});
},

keyboardWillHide (e) {
  this.setState({visibleHeight: Dimensions.get('window').height});
},

...

render: function() {
  return (<View style={{height: this.state.visibleHeight}}>your view code here...</View>);
}

...

それは私のために働いた。ビューは基本的に、キーボードが表示されると縮小し、非表示になると元に戻ります。


また、このソリューションは適切に機能します(RN 0.21.0)stackoverflow.com/a/35874233/3346628
pomo

自分の代わりにthis.keyboardWillHide.bind(this)を使用してください
animekun

代わりにキーボードを使用してくださいDeviceEventEmitter
Madura Pradeep


4

多分遅くなるかもしれませんが、最善の解決策は、ネイティブライブラリIQKeyboardManagerを使用することです

IQKeyboardManagerディレクトリをデモプロジェクトからiOSプロジェクトにドラッグアンドドロップするだけです。それでおしまい。また、isToolbarを有効にしたり、AppDelegate.mファイルでテキスト入力とキーボードの間のスペースを設定したりすることもできます。カスタマイズの詳細については、追加したGitHubページのリンクを参照してください。


1
これは優れたオプションです。ReactNative用にラップされたバージョンについては、github.com / douglasjunior / react-native-keyboard- managerも参照してください。インストールは簡単です。
loevborg 2017

3

TextInput.onFocusとScrollView.scrollToを使用しました。

...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
  this.refs.scrollView.scrollTo(width*2/3);
},

2

@Stephen

キーボードが表示されるのとまったく同じ速度で高さをアニメーション化しなくても構わない場合は、LayoutAnimationを使用して、少なくとも高さが所定の位置にジャンプしないようにすることができます。例えば

react-nativeからLayoutAnimationをインポートし、次のメソッドをコンポーネントに追加します。

getInitialState: function() {
    return {keyboardSpace: 0};
  },
   updateKeyboardSpace: function(frames) {
    LayoutAnimation.configureNext(animations.layout.spring);
    this.setState({keyboardSpace: frames.end.height});
  },

  resetKeyboardSpace: function() {
    LayoutAnimation.configureNext(animations.layout.spring);
    this.setState({keyboardSpace: 0});
  },

  componentDidMount: function() {
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
    KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
  },

  componentWillUnmount: function() {
    KeyboardEventEmitter.off(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
    KeyboardEventEmitter.off(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
  },

いくつかのアニメーション例は(私は上の春のものを使用しています):

var animations = {
  layout: {
    spring: {
      duration: 400,
      create: {
        duration: 300,
        type: LayoutAnimation.Types.easeInEaseOut,
        property: LayoutAnimation.Properties.opacity,
      },
      update: {
        type: LayoutAnimation.Types.spring,
        springDamping: 400,
      },
    },
    easeInEaseOut: {
      duration: 400,
      create: {
        type: LayoutAnimation.Types.easeInEaseOut,
        property: LayoutAnimation.Properties.scaleXY,
      },
      update: {
        type: LayoutAnimation.Types.easeInEaseOut,
      },
    },
  },
};

更新:

以下の@sherlockの回答を参照してください。react-native0.11以降、キーボードのサイズ変更は組み込みの機能を使用して解決できます。


2

いくつかのメソッドを組み合わせて、もう少し単純なものにすることができます。

入力にonFocusリスナーをアタッチします

<TextInput ref="password" secureTextEntry={true} 
           onFocus={this.scrolldown.bind(this,'password')}
/>

スクロールダウンメソッドは次のようになります。

scrolldown(ref) {
    const self = this;
    this.refs[ref].measure((ox, oy, width, height, px, py) => {
        self.refs.scrollView.scrollTo({y: oy - 200});
    });
}

これは、スクロールビュー(参照を追加することを忘れないでください)に、フォーカスされた入力の位置-200(キーボードのサイズとほぼ同じ)まで下にスクロールするように指示します。

componentWillMount() {
    this.keyboardDidHideListener = Keyboard.addListener(
      'keyboardWillHide', 
      this.keyboardDidHide.bind(this)
    )
}

componentWillUnmount() {
    this.keyboardDidHideListener.remove()
}

keyboardDidHide(e) {
    this.refs.scrollView.scrollTo({y: 0});
}

ここで、スクロールビューを上部にリセットします。

ここに画像の説明を入力してください


2
@render()メソッドを提供できますか?
valerybodak

0

より単純な方法を使用していますが、まだアニメーション化されていません。「bumpedUp」というコンポーネントの状態があり、デフォルトでは0ですが、textInputがフォーカスを取得すると、次のように1に設定されます。

私のtextInputで:

onFocus={() => this.setState({bumpedUp: 1})}
onEndEditing={() => this.setState({bumpedUp: 0})}

次のように、画面上のすべてのラッピングコンテナーに下マージンと負の上マージンを与えるスタイルもあります。

mythingscontainer: {
  flex: 1,
  justifyContent: "center",
  alignItems: "center",
  flexDirection: "column",
},
bumpedcontainer: {
  marginBottom: 210,
  marginTop: -210,
},

そして、ラッピングコンテナーで、次のようにスタイルを設定します。

<View style={[styles.mythingscontainer, this.state.bumpedUp && styles.bumpedcontainer]}>

したがって、「bumpedUp」状態が1に設定されると、bumpedcontainerスタイルが開始され、コンテンツが上に移動します。

ちょっとハッキーでマージンはハードコードされていますが、動作します:)


0

私はbrysgo回答を使用して、スクロールビューの下部を上げます。次に、onScrollを使用して、スクロールビューの現在の位置を更新します。次に、このReact Native:要素の位置を取得して、textinputの位置を取得しました。次に、入力が現在のビューにあるかどうかを判断するために簡単な計算を行います。次に、scrollToを使用して、最小量とマージンを移動します。それはかなり滑らかです。スクロール部分のコードを次に示します。

            focusOn: function(target) {
                return () => {
                    var handle = React.findNodeHandle(this.refs[target]);
                    UIManager.measureLayoutRelativeToParent( handle, 
                        (e) => {console.error(e)}, 
                        (x,y,w,h) => {
                            var offs = this.scrollPosition + 250;
                            var subHeaderHeight = (Sizes.width > 320) ? Sizes.height * 0.067 : Sizes.height * 0.077;
                            var headerHeight = Sizes.height / 9;
                            var largeSpace = (Sizes.height - (subHeaderHeight + headerHeight));
                            var shortSpace = largeSpace - this.keyboardOffset;
                            if(y+h >= this.scrollPosition + shortSpace) {
                                this.refs.sv.scrollTo(y+h - shortSpace + 20);
                            }
                            if(y < this.scrollPosition) this.refs.sv.scrollTo(this.scrollPosition - (this.scrollPosition-y) - 20 );
                        }
                     );
                };
            },

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.